【问题标题】:Bypass MySQL automatic GMT conversion when selecting TIMESTAMP fields选择 TIMESTAMP 字段时绕过 MySQL 自动 GMT 转换
【发布时间】:2016-10-31 04:50:11
【问题描述】:

互联网上有很多关于转换 MySQL 时间戳、它如何工作等的资料。但我需要的是相反的:知道 MySQL 在 TIMESTAMP 字段中将每个日期时间数据存储为 UTC,我想要的只是直接检索存储的UTC 值,没有 MySQL 与系统/服务器/连接时区混淆日期时间。

你看,我们已经构建了一个简单的 node.js 馈送器,它从几个第三方 MySQL 数据库中读取数据(所以我不能更改它们的时区设置)并将收集到的数据保存到 Elasticsearch,作为“非规范化过程”。由于原始数据来自不同的时区,我需要将它们存储在 UTC 中,这样我就可以轻松地协调进一步的 GET 和聚合。

我知道我可以即时设置连接时区,并且我知道我可以更改在我的节点应用程序中获取的每个时间戳字段,但是由于 MySQL 引擎已经以 UTC 存储时间戳,如果我可以简单地添加任何其他步骤,我为什么要添加任何其他步骤直接获取,无需转换函数或昂贵的数据处理?

简而言之,我想知道:有没有办法绕过 MySQL 自动 GMT 转换?

【问题讨论】:

  • 不能将服务器时区设置为 SET time_zone = UTC;这将解决所有问题

标签: mysql datetime timezone timestamp utc


【解决方案1】:

MySQL 提供了一个UNIX_TIMESTAMP 函数,它返回一个原始整数值。这根本不受时区转换的影响。

SELECT UNIX_TIMESTAMP( timestamp_col ) ...

但这会返回一个原始整数,而不是日期时间。如果需要,客户端需要将其转换为“日期时间”类型的对象。


另一种选择是使用 CONVERT_TZ 函数从会话 time_zone 设置转换为 UTC。

 SELECT CONVERT_TZ( timestamp_col, @@session.time_zone,'+00:00')

但是,这并没有真正“绕过”时区转换。这种方法的一个缺点是,如果会话 time_zone 受到夏令时更改的影响,那么当时钟“回退”一小时时,每年一小时的时间段会产生歧义。例如2015 年 11 月 1 日星期日 2AM CDT 过渡到 2015 年 11 月 1 日星期日 1AM CST。 (从 UTC 转换回来,如果我们在会话 time_zone 中得到 1:30 AM,我们不“知道”那是 CDT 还是 CST。转换回 UTC 不知道它是哪个是。)


另一个选项(我想你已经提到过)是将会话 time_zone 更改为 UTC。然后您可以将 timestamp_col 值返回为 UTC。您可以保存当前的 time_zone 设置,并在完成后将其设置回来,例如

SET @save_session_time_zone := @@session.time_zone ;
SET time_zone = '+00:00' ; 
SELECT timestamp_col ... 
SET time_zone = @save_session_time_zone ;

但是当 MySQL 数据库会话的 time_zone 与客户端的 time_zone 不匹配时,您的客户端连接器可能会进行一些不太有用的转换,就像 JDBC 驱动程序 (MySQL Connector/J) 所做的时髦恶作剧一样。 (这个问题不仅限于返回 UTC;只要客户端的 time_zone 与数据库会话的 time_zone 不匹配,就会出现这个问题。)

【讨论】:

  • 感谢您的详尽回答,@spencer7593。我认为这些是我们必须使用的工具,因为 MySQL 不允许我们简单地获取原始值或在没有任何转换的情况下获取它。这是一种约束,你不觉得吗?
【解决方案2】:

似乎无法从 MySQL 字段中获取原始 UTC 值;每个函数都使用时区设置,无论是 SYSTEM 还是您配置的任何其他函数。

MySQL 强制您使用日期转换的方式至少是非常受限制的。例如,假设您将 MySQL 服务器设置为 GMT -03:00 和 GMT/DST -02:00 的时区,并且您存储的日期时间类似于“2016-07-01 10:00:00”。如果您在 DST 结束后选择此值,您将获得“2016-07-01 09:00:00”。

除非您单独存储 GMT 偏移量,或者您之前知道服务器存储时的时区,否则您无法确定现在是什么时间。

我们使用了第二种方法。我们保存了服务器时区,并使用它来计算偏移量并返回 ISO 日期时间,因此可以轻松进行未来的计算。

DROP FUNCTION IF EXISTS `iso_datetime`;;

CREATE FUNCTION `iso_datetime` (
    p_datetime TIMESTAMP
) RETURNS VARCHAR(25)

READS SQL DATA

BEGIN

    DECLARE _timezone VARCHAR(255) DEFAULT NULL;
    DECLARE _offset VARCHAR(6) DEFAULT NULL;

    SET _timezone = (SELECT timezone FROM network);
    SET _offset = (SELECT SUBSTRING(TIMEDIFF(p_datetime,CONVERT_TZ(p_datetime, _timezone,'UTC')), 1,6));

    RETURN CONCAT(DATE_FORMAT(p_datetime, '%Y-%m-%dT%H:%i:%S'), _offset);

END;

为此,您必须将时区信息加载到 MySQL 中,以便服务器可以为您计算日期的 tz 偏移量。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-10
    • 2019-01-27
    • 2013-08-08
    • 1970-01-01
    • 2015-03-22
    • 2021-08-30
    • 2015-07-30
    • 2015-04-20
    相关资源
    最近更新 更多