如何使用时区信息在MySQL中存储日期时间


76

我有数千张在坦桑尼亚拍摄的照片,我想将每张照片的日期和时间存储在MySQL数据库中。但是,该服务器位于美国,并且在春季夏令时期间(美国),当我尝试存储坦桑尼亚日期时间(在“无效”小时内)时,我遇到了问题。坦桑尼亚不执行夏令时,因此该时间实际上是有效时间。

另外一个复杂的问题是,来自许多不同时区的协作者将需要访问存储在数据库中的日期时间值。我希望他们总是以坦桑尼亚的时间出现,而不是出现在各个合作者所在的当地时间。

我不愿意设置会话时间,因为我知道当某人有时忘记设置会话时间并弄错所有时间会出现问题。而且我无权更改有关服务器的任何内容。

我读过: 夏令时和时区最佳实践以及 MySQL datetime字段和夏令时-我如何引用“额外”小时?将日期时间存储为PHP / MySQL中的UTC

但是他们似乎都没有解决我的特殊问题。我不是SQL专家;设置DATETIME时,有没有办法指定时区?我还没看过 否则,对于如何解决此问题的任何建议将不胜感激。

编辑:这是我遇到的问题的一个示例。我发送命令:

INSERT INTO Images (CaptureEvent, SequenceNum, PathFilename, TimestampJPG) 
VALUES (122,1,"S2/B04/B04_R1/IMAG0148.JPG","2011-03-13 02:49:10")

我得到了错误:

Error 1292: Incorrect datetime value: '2011-03-13 02:49:10' for column 'TimestampJPG'

该日期和时间存在于坦桑尼亚,但数据库所在的美国不存在。


13
您不希望将时区信息存储在数据库中。将所有日期/时间数据存储为UTC,并始终在应用程序层上进行时区偏移量调整。
2013年

3
@MarcellFülöp:我一直看到人们说“将其存储为UTC”,但是我不明白那是什么意思。如何将日期时间“作为UTC”存储?据我所知,我只能插入YYYY-MM-DD HH:MM:SS形式的东西。我在哪里告诉数据库它是UTC?
mkosmala 2013年

你不知道 默认情况下,MySQL将在内部使用系统时区,但可以为MySQL服务器全局或什至为每个事务定义不同的时区。当您插入日期时,无法在MySQL中定义时区以及日期字符串。像“ 2013-11-10 00:00”之类的日期是指自该时期以来的时间点。您将其存储起来,并且知道服务器所在的时区。然后,在检索它时,可以进行必要的调整,以将该日期从服务器的时区转换为客户端的时区。
marekful

Answers:


70

你说:

我希望他们总是以坦桑尼亚的时间出现,而不是出现在各个合作者所在的当地时间。

如果是这种情况,那么你应该使用UTC。您需要做的就是DATETIME在MySQL中使用类型而不是TIMESTAMP类型。

从MySQL文档

MySQL将TIMESTAMP值从当前时区转换为UTC以进行存储,然后从UTC转换为当前时区以进行检索。(对于其他类型,例如,不会发生这种情况DATETIME。)

如果您已经在使用一种DATETIME类型,则一定不要在本地时间开始设置它。您将需要减少对数据库的关注,而将更多的精力放在应用程序代码上(在此未显示)。该问题和解决方案将根据语言而变化很大,因此请确保使用您的应用程序代码的适当语言标记该问题。


2
我正在使用DATETIME。但是,如果我尝试设置一个在美国不存在的时间(但在坦桑尼亚不存在),则数据库将引发错误。我已经对问题进行了编辑,以举例说明遇到的麻烦。我的应用程序代码恰好是用Python编写的,但现在实际上只是SQL代码的包装。我很高兴用Python进行转换,但是不确定它们应该是什么。
mkosmala

2
您能否然后显示您的python代码,以便我们可以看到您如何构建SQL查询?您可能只在python中处理朴素vs知道日期时间。和你正面,你的TimestampJPG列是DATETIME在MySQL数据库架构类型,而不是一个TIMESTAMP类型?
马特·约翰逊·品脱

1
精氨酸 你是对的。我将数据库中的所有其他TIMESTAMP都更改为DATETIME,但是以某种方式错过了这一点。这样就解决了问题。谢谢。
mkosmala 2014年

19
这是MySQL的另一个地方,令您感到遗憾的是不使用Postgresql。;)
poige 2014年

7
@poige为什么您说Postgresql在这里会更好?还将在存储之前将时间戳转换为UTC。
Anshul 2015年

25

这里的答案都没有击中头。

如何使用时区信息在MySQL中存储日期时间

使用两列:DATETIMEVARCHAR来保存时区信息,该信息可以采用多种形式:

时区位置,例如America/New_York是最高的数据的保真度。

一个时区的缩写,如PST为下一个最高的保真度。

时间偏移,例如-2:00是数据的在这方面的最小量。

一些要点:

  • 避免使用,TIMESTAMP因为它限于2038年,而MySQL将其与服务器时区相关联,这可能是不希望的。
  • 时间偏移量不应天真地存储在INT字段中,因为存在半小时和四分之一小时的偏移量。

如果对您的用例而言,让MySQL按时间顺序这些日期进行比较排序很重要,DATETIME则存在问题:

'2009-11-10 11:00:00 -0500''2009-11-10 10:00:00 -0700'在“即时”方面早于,但是当插入时,它们会以其他方式排序DATETIME

您可以自己转换为UTC。在上面的示例中,您将分别拥有
'2009-11-10 16:00:00''2009-11-10 17:00:00',它们将正确排序。检索数据时,您将使用时区信息将其还原为原始格式。

我非常喜欢的一项建议是包含列:

  • local_time DATETIME
  • utc_time DATETIME
  • time_zone VARCHAR(X)X适合您在那里存储的数据类型。(我会选择64个字符作为时区/位置。)

3列方法的一个优点是它很明确:只有一DATETIME列,您无法一眼就知道插入之前是否已将其转换为UTC。


关于通过时区/缩写/偏移量的准确性下降:

  • 如果您具有用户的时区/位置(例如)America/Juneau,则可以准确地知道过去或将来任何时间的挂钟时间(除非更改在该位置处理夏令时的方式)。DST的起点/终点以及是否完全使用DST取决于位置,因此这是唯一可靠的方法。
  • 如果您使用时区缩写(例如MST)((山地标准时间))或普通偏移量(例如)-0700,则将无法预测过去或将来的挂钟时间。例如,在美国,科罗拉多州和亚利桑那州都使用MST,但亚利桑那州未遵守DST。因此,如果用户14:00 -0700在冬季在上载猫的照片,他是在亚利桑那州还是在加利福尼亚州?如果您在该日期前精确地添加了六个月,那么该日期14:00还是该日期13:00

当您的应用程序具有时间,日期或调度作为核心功能时,考虑这些因素很重要。


参考文献:


2
我看不到存储本地时间的好处。恕我直言,存储日期时间值本身的最简洁方法是使用UTC。这样可以避免在比较日期时间时造成任何混乱。如果有理由知道当地的特定时间,请记录一个LOCATION。然后,应用逻辑可以将该位置应用于已知的本地时间(例如,在工作时间吗?),但是将DateTime本身保留在UTC中。例如,我们在瑞典的设施被定义为在瑞典的时区;我们已经有一个字段(在事件记录中)说“在什么设施上”,因此不需要为每个事件记录分别添加时区。
制造商史蒂夫

2
如果某个位置的某人正在为另一个位置输入事件,则在UTC中保留日期的好处就显而易见了。如果纽约的技术人员报告了瑞典的事件,那么“当地时间”是纽约还是瑞典?通过存储UTC时间来避免这种混乱。技术人员的位置和该设施的位置,附加到事件,允许它在被观看或者“本地时间”,如所期望,没有任何混乱-简单地通过指定POV。或者您可能对它在您所在的时间发生的时间感兴趣,这可能是第三个“本地时间”。通过位置动态指定“本地偏移”。
制造商史蒂夫

3
@ToolmakerSteve如果再次看过我的答案,你会看到我的主要观点是,你需要存储timelocation在单独的列。我还谈到了存储UTC时间。如果您只想存储UTC时间,因为您的locations是复数形式和/或在另一个表中,那就太好了。然后,我唯一的建议是utc_time为清楚起见对其进行标记。
马特·麦克

如果我需要时间来处理不是数据库元数据的任何数据点,则也可以使用这种方法。例如,我存储一些跨多个时区的旅行数据。我保留四列(start_dt,start_tz,end_dt,end_tz),但为简单起见,我也进行了计算,duration因此无需一直进行这些计算。如果需要在用户的时区中显示它们,请在应用程序层中进行显示。尽管有时我会考虑更改为存储UTC +时区,以便我可以更轻松地将UTC传递给应用程序
Moseleyi

6

MySQL存储的DATETIME没有时区信息。假设您将“ 2019-01-01 20:00:00”存储到DATETIME字段中,当您检索该值时,预计会知道它属于哪个时区。

因此,在您的情况下,将值存储到DATETIME字段时,请确保它是坦桑尼亚时间。然后,当您解决问题时,将是坦桑尼亚的时间。好极了!

现在,毛茸茸的问题是:当我执行INSERT / UPDATE时,如何确定值是坦桑尼亚时间?两种情况:

  1. 你做INSERT INTO table (dateCreated) VALUES (CURRENT_TIMESTAMP or NOW())

  2. 您执行INSERT INTO table (dateCreated) VALUES (?),然后从您的应用程序代码中指定当前时间。

情况1

MySQL将采用当前时间,比方说坦桑尼亚时间为“ 2019-01-01 20:00:00”。那么MySQL将其转换为UTC,它出来为“2019年1月1日17:00:00”,并存储值到该字段中。

那么,如何将坦桑尼亚时间(20:00:00)存储到现场?这是不可能的。从该字段读取时,您的代码将需要期望UTC时间。

情况#2

这取决于您传递的值的类型?。如果您传递字符串“ 2019-01-01 20:00:00”,那么这对您有好处,这正是将存储在数据库中的内容。如果传递某种Date对象,则将取决于数据库驱动程序如何解释该Date对象,最终取决于它向MySQL提供的用于存储的'YYYY-MM-DD HH:mm:ss'字符串。数据库驱动程序的文档应告诉您。


6

您描述的所有症状表明您从不告诉MySQL使用哪个时区,因此它默认为系统的时区。想一想:如果只有,它'2011-03-13 02:49:10'怎么能猜到这是坦桑尼亚的本地日期?

据我所知,MySQL没有提供任何语法来指定日期中的时区信息。您必须根据每个连接更改它;就像是:

SET time_zone = 'EAT';

如果这不起作用(要使用命名区域,则需要将服务器配置为使用命名区域,通常情况并非如此),可以使用UTC偏移量,因为坦桑尼亚在撰写本文时并未遵守夏令时,但是当然这不是最佳选择:

SET time_zone = '+03:00';

15
您不能仅仅因为UTC偏移量而打耳光,因为它忽略了夏令时。您的时间可能很容易结束一个小时,而几乎没有希望进行更正!我知道SQL Server中的功能会将UTC转换为您的时区,但是它没有考虑夏令时的任意变化。将其存储在UTC零偏移量中并在应用程序处进行转换-这样可以非常干净地解决许多问题,但是您将很难在临时查询中正确转换日期。
肖恩·安德森

现在可以至少直接在日期中指定时区偏移量,例如:'2020-01-01 10:10:10 + 05:30'(来自dev.mysql.com/doc/refman/8.0/ zh_CN / datetime.html)。因为时区偏移量是针对特定的日期和时间的,所以不要指定特定的时区(也就是它们的夏时制规则)并不重要。因为这样您就可以精确地指定从该日期起如何计算UTC,MySQL会将UTC保存为UTC。仅当您要计算多个日期(DST而不是DST)的UTC时,才需要“ EAT”之类的时区。
多米尼克

0

我曾经也遇到过这样的问题,我需要保存供不同合作者使用的数据,最后我将时间存储为unix时间戳格式,该格式表示自1970年1月以来的秒数,它是整数格式。坦桑尼亚的今天日期和时间示例是Friday, September 13, 2019 9:44:01 PM在UNIX时间戳中存储时1568400241

现在,当读取数据时,只需使用类似php或任何其他语言的内容,然后从UNIX时间戳中提取日期。php的示例将是

echo date('m/d/Y', 1568400241);

这甚至使与其他协作者在不同位置存储数据变得更加容易。他们可以使用自己的gmt偏移量将日期简单地转换为unix时间戳,并以整数格式存储,而在输出时只需使用


1
从技术上讲,您不是将日期和时区信息一起存储,而是要丢弃时区信息。这基本上是本土日期类型在MySQL ;-)已经做
阿尔瓦罗·冈萨雷斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.