94

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

其他复杂情况是,来自许多不同时区的合作者需要访问存储在数据库中的日期时间值。我希望它们始终以坦桑尼亚时间出现,而不是各种合作者所在的当地时间。

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

我已阅读: 夏令时和时区最佳实践以及 MySQL 日期时间字段和夏令时——我如何引用“额外”小时?并将 日期时间存储为 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'

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

4

5 回答 5

80

你说:

我希望它们始终以坦桑尼亚时间出现,而不是各种合作者所在的当地时间。

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

从 MySQL 文档

MySQL 将TIMESTAMP值从当前时区转换为 UTC 进行存储,然后从 UTC 转换回当前时区进行检索。(这不会发生在其他类型中,例如DATETIME.)

如果您已经在使用一种DATETIME类型,那么您必须不是在本地时间开始设置它。您将需要较少地关注数据库,而更多地关注您的应用程序代码 - 您没有在这里展示。问题和解决方案会因语言而异,因此请务必使用应用程序代码的适当语言标记问题。

于 2013-11-07T21:08:46.263 回答
41

这里的答案都没有一针见血。

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

使用两列:DATETIME和 aVARCHAR来保存时区信息,可能有多种形式:

最高数据保真度的时位置。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,您可以准确地知道他们在过去或未来任何时候的挂钟时间(除非更改该位置处理夏令时的方式)。夏令时的起点/终点,以及是否使用,都取决于位置,所以这是唯一可靠的方法。
  • 如果您有诸如 MST、(山地标准时间)之类的时区缩写或诸如 之类的普通偏移量-0700,您将无法预测过去或未来的挂钟时间。例如,在美国,科罗拉多州和亚利桑那州都使用 MST,但亚利桑那州不遵守 DST。14:00 -0700因此,如果用户在冬季上传他的猫照片,他是在亚利桑那州还是加利福尼亚州?如果你在那个日期之前加上了六个月,是用户14:00还是13:00用户?

当您的应用程序将时间、日期或调度作为核心功能时,这些事情很重要。


参考:

于 2019-11-23T02:30:41.750 回答
11

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-01-01 17:00:00',并将值存储到字段中。

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

案例#2

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

于 2019-01-07T17:51:57.333 回答
7

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

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

SET time_zone = 'EAT';

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

SET time_zone = '+03:00';
于 2013-11-11T11:04:01.707 回答
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 时间戳,并将其存储为整数格式,并且在输出时只需使用

于 2019-09-13T18:52:55.540 回答