我有数千张在坦桑尼亚拍摄的照片,我想把每张照片的拍摄日期和时间存储在MySQL数据库中。但是,服务器位于美国,当我试图存储一个坦桑尼亚日期时间时,会遇到问题,该时间在春季夏令时(在美国)的"无效"时间内。坦桑尼亚不做DST,所以时间实际上是一个有效的时间。
另外一个复杂的情况是,来自许多不同时区的合作者需要访问数据库中存储的日期时间值。我希望他们总是以坦桑尼亚时代的形式出现,而不是在各个合作者所处的当地时代。
我不愿意设置会话时间,因为我知道当有人某个时候忘记设置会话时间而把时间搞错时,会出现问题。我无权更改服务器的任何内容。
我读过:夏令时和时区最佳实践mysql日期时间字段和夏令时——如何引用"额外"小时?和将datetime存储为php/mysql中的UTC
但他们似乎都没有解决我的特殊问题。我不是SQL专家;在设置日期时间时是否有指定时区的方法?我没见过。否则,任何关于如何处理这个问题的建议都将受到极大的赞赏。
编辑:下面是我遇到的问题的一个例子。我发送命令:
1 2
| INSERT INTO Images (CaptureEvent , SequenceNum , PathFilename , TimestampJPG )
VALUES (122,1,"S2/B04/B04_R1/IMAG0148.JPG","2011-03-13 02:49:10") |
我得到了错误:
这个日期和时间存在于坦桑尼亚,但不存在于数据库所在的美国。
- 您不应该希望在数据库中存储时区信息。将所有日期/时间数据存储为UTC,并始终在应用程序层上调整时区偏移量。
- @Marcellf&252;l&246;p:我一直看到人们说"将其存储为UTC",但我不明白这意味着什么。如何将日期时间存储为"UTC"?据我所知,我所能做的就是插入一些格式为YYYY-MM-DD hh:mm:ss的内容。我应该在哪里告诉数据库它是UTC?
- 不需要。默认情况下,MySQL将在内部使用系统时区,但可以为MySQL服务器全局或甚至每个事务定义不同的时区。插入日期时,不可能在MySQL中定义时区和日期字符串。像"2013-11-10 00:00"这样的日期是指从新纪元开始的时间点。你存储它,你知道你的服务器在哪个时区。然后,当您检索它时,您可以进行必要的调整,将该日期从服务器的时区转换为客户机的时区。
你说:
I want them to always come out as Tanzanian time and not in the local times that various collaborator are in.
如果是这种情况,则不应使用UTC。您只需要在MySQL中使用DATETIME类型,而不是TIMESTAMP类型。
从MySQL文档中:
MySQL converts TIMESTAMP values from the current time zone to UTC for storage, and back from UTC to the current time zone for retrieval. (This does not occur for other types such as DATETIME.)
如果已经在使用DATETIME类型,则不能在本地时间开始设置。您将需要较少地关注数据库,而更多地关注您的应用程序代码——这里没有显示这些代码。根据语言的不同,问题和解决方案会有很大的不同,因此请确保用应用程序代码的适当语言标记问题。
- 我正在使用日期时间。但是,如果我试图设置一个在美国不存在的时间(但在坦桑尼亚存在),数据库就会抛出一个错误。我已经编辑了我的问题,以举例说明我遇到的麻烦。我的应用程序代码碰巧是用Python编写的,但现在它实际上只是一个围绕SQL代码的包装器。我很乐意在Python中进行转换,但不确定它们应该是什么。
- 那么,您能展示一下您的python代码吗,这样我们就可以看到您是如何构建SQL查询的了?在Python中,您可能只是在处理幼稚与敏感的日期时间。你肯定你的TimestampJPG列是mysql数据库模式中的DATETIME类型而不是TIMESTAMP类型吗?
- 精氨酸你说得对。我将数据库中的所有其他时间戳都改为datetime,但不知何故错过了这个时间戳。这就解决了问题。谢谢。
- 这是另一个让你感到抱歉的地方,MySQL没有使用PostgreSQL代替。;)
- @你为什么说PostgreSQL在这里会更好?它还可以在存储之前将时间戳转换为UTC。
您描述的所有症状都表明您永远不会告诉MySQL使用什么时区,所以它默认为系统的时区。想一想:如果它只剩下'2011-03-13 02:49:10',怎么能猜到它是当地的坦桑尼亚日期?
据我所知,MySQL没有提供任何语法来指定日期中的时区信息。您必须根据每个连接对其进行更改;例如:
如果这不起作用(要使用命名区域,您需要将服务器配置为这样做,但通常情况并非如此),您可以简单地使用UTC偏移量:
1
| SET time_zone = '+03:00'; |
- 但是,您不能只使用UTC偏移量,因为它忽略了夏令时。你的时间很容易就结束了一个小时,而你根本就没有希望改正它!我知道SQL Server中的函数会将UTC转换为您的时区,但它不考虑夏令时的任意变化。存储在UTC的零偏移量中,并在应用程序中转换--这可以非常清楚地解决许多问题,但是在特殊查询中正确地转换日期将是非常困难的。
MySQL存储的日期时间不包含时区信息。假设您将"2019-01-01 20:00:00"存储到一个日期时间字段中,当您检索该值时,您应该知道它属于哪个时区。
所以在您的例子中,当您将一个值存储到日期时间字段中时,请确保它是坦桑尼亚时间。当你把它拿出来的时候,坦桑尼亚时间到了。哎呀!
现在,最棘手的问题是:当我进行插入/更新时,如何确保值是坦桑尼亚时间?两例:
你做的是INSERT INTO table (dateCreated) VALUES (CURRENT_TIMESTAMP or NOW())。
您执行INSERT INTO table (dateCreated) VALUES (?),并从应用程序代码中指定当前时间。
第1案
MySQL将采用当前时间,假设是"2019-01-01 20:00:00"坦桑尼亚时间。然后mysql会将其转换为utc,结果是"2019-01-01 17:00:00",并将该值存储到字段中。
那么,你如何获得坦桑尼亚时间,也就是20:00:00,储存到野外?这是不可能的。从该字段读取时,您的代码需要预计UTC时间。
第2案
这取决于作为?传递的值的类型。如果您通过字符串"2019-01-01 20:00:00",那么这对您很好,这正是将存储到数据库的内容。如果传递某种类型的日期对象,那么它将取决于db驱动程序如何解释该日期对象,以及它为MySQL提供的最终"YYYY-MM-DD hh:mm:ss"字符串。数据库驱动程序的文档应该告诉您。