【问题标题】:How to group by date, accounting for timezones and DST?如何按日期分组,考虑时区和 DST?
【发布时间】:2015-03-16 21:27:35
【问题描述】:

我的数据库中存储了一堆事件。每个事件都有一个开始时间,以 UTC 格式存储为 DATETIME。每个用户都可以选择自己的时区。

我想显示一个日历,它只显示该月每一天的事件数。我不能GROUP BY DATE(start_dt) 因为用户的一天不是[必然]从 00:00:00 UTC 开始的。此外,用户的时区可能会在月中进行 DST 切换。

那么我唯一的选择是获取给定月份的每个事件,然后用我的服务器端语言对它们进行分组和计数吗?

【问题讨论】:

    标签: mysql datetime timezone


    【解决方案1】:

    你可以试试

    SELECT ... FROM ... GROUP BY DATE(CONVERT_TZ(start_dt,'UTC','America/Vancouver'))
    

    请注意,您需要先load the timezone data into MySQL 才能使用。

    【讨论】:

    • 或者使用数字偏移量作为CONVERT_TZ() (manual) 的第三个参数,这样就不需要时区表,例如。 CONVERT_TZ('2004-01-01 12:00:00','UTC','+10:00');
    • @RandomSeed 问题在于夏令时。月初的偏移量可能与月底的偏移量不同。
    • @RandomSeed 你在你的例子中犯了一个小错误。应该是:CONVERT_TZ('2004-01-01 12:00:00','+00:00','+10:00');,因为使用UTC 而不是+00:00 仍然会导致NULL 结果。
    【解决方案2】:

    很遗憾,DATETIME 列存储时没有时区信息:

    当前时区设置不会影响 UTC_TIMESTAMP() 等函数显示的值或 DATE、TIME 或 DATETIME 列中的值。这些数据类型中的值也不以 UTC 格式存储;仅当从 TIMESTAMP 值转换时,时区才适用于它们。如果您想要 DATE、TIME 或 DATETIME 值的特定于区域设置的算术,请将它们转换为 UTC,执行算术,然后再转换回来。 [MySQL Docs]

    幸运的是,这确实意味着日期/时间函数应该与时区支持一起使用,但仅限于 TIMESTAMP 列。一旦进入TIMESTAMP 列,日期/时间函数应该能够考虑@@session.time_zone,您可以根据需要更改它以获得不同的时区。

    因此,您的选择是让您的表包含 DATETIME 列,并自己进行所有分组,或者将这些列转换为 TIMESTAMP 并让 MySQL 来做 - 但如果这些列已经处于不同且不一致的时区,你会为你完成你的工作......

    【讨论】:

    • 您只需要记住 tzdata 不是一成不变的,一旦您将某些日期转换为时间戳,一旦更改时区规则,您就不需要正确恢复它们。
    【解决方案3】:

    您可以在中间切换到用户 tz:

      SET @@session.time_zone = "+05:00"; 
      select ... from .. group by MONTH(datefield);
      SET @@session.time_zone = @@global.time_zone;
    

    刚刚找到:How do I set the time zone of MySQL?

    【讨论】:

    • 更改时区实际上不会影响我的DATETIME 字段吗?
    • 不幸的是它不会。来自 MySQL 文档:“当前时区设置不会影响函数显示的值,例如 UTC_TIMESTAMP() 或 DATE、TIME 或 DATETIME 列中的值。这些数据类型中的值也不以 UTC 存储;时区适用于它们仅在从 TIMESTAMP 值转换时。如果您想要 DATE、TIME 或 DATETIME 值的特定于区域设置的算术,请将它们转换为 UTC,执行算术,然后再转换回来。"
    • 不!试试SET @@session.time_zone = "+03:00"; select now() from dual;它不会改变你的时钟...
    • @AxelAmthor 那是个问题。按DATE() 分组将与我没有设置时区完全相同。
    • 据我所知,让 MySQL 执行区域设置感知操作的唯一方法是使用 TIMESTAMP 列执行所有操作;那么会话时区将用于分组等。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-25
    • 2016-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-06
    相关资源
    最近更新 更多