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

kulphzqa  于 2022-11-21  发布在  Mysql
关注(0)|答案(7)|浏览(169)

我有数千张在坦桑尼亚拍摄的照片,我想将每张照片的拍摄日期和时间存储在MySQL数据库中。然而,服务器位于美国,当我试图存储坦桑尼亚的日期时间时,遇到了问题,该日期福尔斯 Spring 夏令时(美国)的“无效”小时内。坦桑尼亚不执行夏令时,因此该时间实际上是有效时间。
其他复杂的情况是,有来自许多不同时区的合作者,他们将需要访问存储在数据库中的日期-时间值。我希望它们 * 总是 * 显示为坦桑尼亚时间,而不是不同合作者所在的当地时间。
我不愿意设置会话时间,因为我知道有时候有人忘记设置会话时间,而得到的时间都是错误的,这会有问题。而且我没有权限更改服务器的任何内容。
我已经读过了
但它们似乎都没有解决我的问题。在设置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'

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

qacovj5a

qacovj5a1#

你说:
我希望他们总是出来作为坦桑尼亚时间,而不是在当地时间,各种合作者都在。
如果是这样的话,那么你就不应该使用UTC。你所需要做的就是在MySQL中使用DATETIME类型,而不是TIMESTAMP类型。
From the MySQL documentation
MySQL将TIMESTAMP值从当前时区转换为UTC以进行存储,然后将其从UTC转换回当前时区以进行检索。(对于其他类型,如DATETIME,则不会发生这种情况。)
如果您 * 已经 * 使用DATETIME类型,那么您一定没有在本地时间开始设置它。您需要少关注数据库,多关注您的应用程序代码-您在这里没有显示。问题和解决方案将根据语言的不同而有很大差异,所以请确保使用您的应用程序代码的适当语言来标记问题。

sdnqo3pr

sdnqo3pr2#

这里没有一个答案是一针见血的。
如何在MySQL中使用时区信息存储日期时间
使用两列:DATETIME,以及VARCHAR,用于保存时区信息,时区信息可以是以下几种形式:
timezone或 * 位置 *(如America/New_York)是最高的数据保真度。
timezone abbreviation(如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',这将正确排序。在检索数据时,您将使用时区信息将其还原为原始形式。
我非常喜欢的一个recommendation有 * 三 * 列:

  • local_time DATETIME
  • utc_time DATETIME
  • time_zone VARCHAR(X),其中X表示您在其中存储的数据类型。(我会选择64个字符表示时区/位置。)

3列方法的一个优点是它是显式的:对于单个DATETIME列,您无法一眼看出它在插入之前是否已转换为UTC。
关于准确性随时区/缩写/偏移量的下降:

  • 如果您有使用者的timezone/location(例如America/Juneau),您就可以精确地知道他们在过去或未来任何时间点的挂钟时间(除非变更该位置行程日光节约的方式)。DST的开始/结束点,以及是否使用它,都取决于位置,因此这是唯一可靠的方式。
  • 如果您有timezone abbreviation(如MST),(山地标准时间)或-0700之类的普通偏移量,您将无法预测过去或未来的挂钟时间。例如,在美国,科罗拉多州和亚利桑那州都使用MST,但亚利桑那州不遵守DST。因此,如果用户在冬季月份上传了14:00 -0700的猫照片,他在亚利桑那州还是加州州?如果在该日期上加上六个月,用户的日期是14:00还是13:00

当您的应用程序将时间、日期或调度作为核心功能时,这些事情是需要考虑的。
参考文献:

(披露:我没有读这整篇文章。)

xesrikrc

xesrikrc3#

MySQL存储DATETIME * 时没有 * 时区信息。假设您将'2019-01-01 20:00:00'存储到DATETIME字段中,当您检索该值时,您 * 应该知道 * 它属于哪个时区。
因此,在您的情况下,当您将值存储到DATETIME字段中时,请确保它是坦桑尼亚时间。然后,当您将它取出时,它将是坦桑尼亚时间。耶!
现在,棘手的问题是:当我执行INSERT/UPDATE时,如何确保值是坦桑尼亚时间?两种情况:
1.你做INSERT INTO table (dateCreated) VALUES (CURRENT_TIMESTAMP or NOW())
1.执行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的内容。如果你传递某种类型的Date对象,那么这将取决于 * db驱动程序如何解释该Date对象 *,以及最终它提供给MySQL存储的'YYYY-MM-DD HH:mm:ss'字符串。db驱动程序的文档应该告诉你。

pxiryf3j

pxiryf3j4#

您所描述的所有症状都表明您从未告诉MySQL使用哪个时区,因此它默认为系统时区。如果它只有'2011-03-13 02:49:10',它怎么能猜出这是坦桑尼亚本地日期?
据我所知,MySQL没有提供任何语法来指定日期中的时区信息。类似于:

SET time_zone = 'EAT';

如果这不起作用(要使用命名区域,您需要the server has been configured来完成,但通常情况下并非如此),您可以使用UTC偏移量,因为在编写本文时Tanzania does not observe daylight saving time,但当然这不是最佳选择:

SET time_zone = '+03:00';
oalqel3c

oalqel3c5#

从**MySQL 8.0.19**开始,您可以在将TIMESTAMPDATETIME值插入数据表时指定时区位移。

偏移量附加到日期时间文字的时间部分,中间没有空格,并且使用与设置time_zone系统变量相同的格式,但以下情况除外:

  • 对于小于10的小时值,需要前导零。
  • 拒绝值“-00:00”。
  • 不能使用诸如“EET”和“Asia/Shanghai”之类的时区名称;“SYSTEM”也不能用于此上下文中。

插入的值的月部分、日部分或两者都不能为零。从MySQL 8.0.22开始强制执行此操作,无论服务器SQL模式设置如何。

示例:

此范例说明如何使用不同的time_zone设定,将具有时区位移的日期时间值插入TIMESTAMPDATETIME数据行,然后撷取它们:

mysql> CREATE TABLE ts (
    ->     id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
    ->     col TIMESTAMP NOT NULL
    -> ) AUTO_INCREMENT = 1;

mysql> CREATE TABLE dt (
    ->     id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    ->     col DATETIME NOT NULL
    -> ) AUTO_INCREMENT = 1;

mysql> SET @@time_zone = 'SYSTEM';

mysql> INSERT INTO ts (col) VALUES ('2020-01-01 10:10:10'),
    ->     ('2020-01-01 10:10:10+05:30'), ('2020-01-01 10:10:10-08:00');

mysql> SET @@time_zone = '+00:00';

mysql> INSERT INTO ts (col) VALUES ('2020-01-01 10:10:10'),
    ->     ('2020-01-01 10:10:10+05:30'), ('2020-01-01 10:10:10-08:00');

mysql> SET @@time_zone = 'SYSTEM';

mysql> INSERT INTO dt (col) VALUES ('2020-01-01 10:10:10'),
    ->     ('2020-01-01 10:10:10+05:30'), ('2020-01-01 10:10:10-08:00');

mysql> SET @@time_zone = '+00:00';

mysql> INSERT INTO dt (col) VALUES ('2020-01-01 10:10:10'),
    ->     ('2020-01-01 10:10:10+05:30'), ('2020-01-01 10:10:10-08:00');

mysql> SET @@time_zone = 'SYSTEM';

mysql> SELECT @@system_time_zone;
+--------------------+
| @@system_time_zone |
+--------------------+
| EST                |
+--------------------+

mysql> SELECT col, UNIX_TIMESTAMP(col) FROM dt ORDER BY id;
+---------------------+---------------------+
| col                 | UNIX_TIMESTAMP(col) |
+---------------------+---------------------+
| 2020-01-01 10:10:10 |          1577891410 |
| 2019-12-31 23:40:10 |          1577853610 |
| 2020-01-01 13:10:10 |          1577902210 |
| 2020-01-01 10:10:10 |          1577891410 |
| 2020-01-01 04:40:10 |          1577871610 |
| 2020-01-01 18:10:10 |          1577920210 |
+---------------------+---------------------+

mysql> SELECT col, UNIX_TIMESTAMP(col) FROM ts ORDER BY id;
+---------------------+---------------------+
| col                 | UNIX_TIMESTAMP(col) |
+---------------------+---------------------+
| 2020-01-01 10:10:10 |          1577891410 |
| 2019-12-31 23:40:10 |          1577853610 |
| 2020-01-01 13:10:10 |          1577902210 |
| 2020-01-01 05:10:10 |          1577873410 |
| 2019-12-31 23:40:10 |          1577853610 |
| 2020-01-01 13:10:10 |          1577902210 |
+---------------------+---------------------+

注意事项:

  • 遗憾的是,选择日期时间值时不会显示偏移量,即使插入时使用了偏移量。
  • 支援的位移值范围是-13:59到+14:00(含)。
  • 包含时区偏移量的日期时间文本被预准备语句接受为参数值。
  • MySQL将TIMESTAMP值从当前时区转换为UTC以进行存储,然后将其从UTC转换回当前时区以进行检索。(对于其他类型,如DATETIME,则不会发生这种情况。)
a0zr77ik

a0zr77ik6#

我曾经也遇到过这样一个问题,我需要保存不同合作者使用的数据,最后我以unix时间戳的形式存储时间,它表示自1970年1月以来的秒数,是一种整数格式。例如,坦桑尼亚的今天的日期和时间是Friday, September 13, 2019 9:44:01 PM,当存储在unix时间戳中时将是1568400241
现在阅读数据时,只需使用php或任何其他语言,从unix时间戳中提取日期。

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

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

xesrikrc

xesrikrc7#

你不能...你会发现很多答案说你“这是不必要的,存储使用UTC”,但它是:您需要存储带时区的日期时间,而MySQL不能...
我在Postgres工作了10年,所有这类问题都不存在(日期时间和时区的管理没有摩擦,您可以透明地存储和比较不同时区中表示的日期时间,ISOString格式的管理很自然,等等...)。
我实际上在MariaDB工作,我不明白为什么在2022年,在一个全球化的世界,MySQL仍然不支持每值时区。

相关问题