MySQL的日期时间字段和夏令时间 - 我如何引用“额外”小时?(MySQL datetime fi

2019-09-02 08:55发布

我使用的是美国/纽约时区。 在秋季,我们“后退” - 一小时有效地在凌晨2点“获得”一小时。 在过渡点处会发生以下情况:

这是1时59分零零秒-04:00
然后1分钟后变成:
01:00:00 -05:00

所以,如果你简单地说“凌晨1:30”这是暧昧是否或不是你指的是在第一时间1:30左右滚动或第二。 我试图调度数据保存到MySQL数据库,不能确定如何正确保存的时间。

这里的问题:
“2009-11-01 00:30:00” 在内部存储为2009-11-01 00:30:00 -04:00
“2009-11-01 01:30:00”在内部存储为2009-11-01 01:30:00 -05:00

这是罚款和相当的预期。 但我怎么救什么01:30:00 -04:00? 该文件没有显示指定偏移,因此,当我试图指定偏移它已经正式忽略任何支持。

我认为唯一的解决方案包括服务器设置为不使用夏令时的时区,做我的脚本进行必要的转换(我使用PHP本)。 但是,这似乎并不像它应该是必要的。

非常感谢您的任何建议。

Answer 1:

MySQL的日期类型,坦率地说,打破,正确,除非你的系统设置为常数偏移时区,像UTC或GMT-5不能存储所有倍。 (我使用MySQL 5.0.45)

这是因为你不能在一个小时内的任何时候存储夏令时之前结束 。 不管你如何输入日期,如果他们是在开关后的一小时内每一个日期函数会将这些时间。

我的系统的时区为America/New_York 。 让我们试着存储1257051600(2009 06:00:00 +0100太阳年11月1)。

下面是使用专有的间隔语法:

SELECT UNIX_TIMESTAMP('2009-11-01 00:00:00' + INTERVAL 3599 SECOND); # 1257051599
SELECT UNIX_TIMESTAMP('2009-11-01 00:00:00' + INTERVAL 3600 SECOND); # 1257055200

SELECT UNIX_TIMESTAMP('2009-11-01 01:00:00' - INTERVAL 1 SECOND); # 1257051599
SELECT UNIX_TIMESTAMP('2009-11-01 01:00:00' - INTERVAL 0 SECOND); # 1257055200

即使FROM_UNIXTIME()将不会返回准确的时间。

SELECT UNIX_TIMESTAMP(FROM_UNIXTIME(1257051599)); # 1257051599
SELECT UNIX_TIMESTAMP(FROM_UNIXTIME(1257051600)); # 1257055200

奇怪的是,DATETIME仍然 “丢失”的一小时内存储和返回(仅字符串形式!)倍DST开始时(如2009-03-08 02:59:59 )。 但在任何MySQL的函数中使用这些日期是有风险的:

SELECT UNIX_TIMESTAMP('2009-03-08 01:59:59'); # 1236495599
SELECT UNIX_TIMESTAMP('2009-03-08 02:00:00'); # 1236495600
# ...
SELECT UNIX_TIMESTAMP('2009-03-08 02:59:59'); # 1236495600
SELECT UNIX_TIMESTAMP('2009-03-08 03:00:00'); # 1236495600

外卖:如果您需要存储,并在一年中检索一次,你有一些不良的选项:

  1. 设置系统时区为GMT +一些常数偏移。 例如UTC
  2. 存储日期为int(亚伦发现,TIMESTAMP甚至不可靠)

  3. 假装DATETIME类型有一定的常数偏移时区。 例如,如果你是America/New_York ,转换您的日期到MySQL的 GMT-5 ,然后保存为一个DATETIME(这原来是必不可少的:见亚伦的答案)。 然后,你必须使用MySQL的日期/时间的功能会非常谨慎,因为一些假设你的价值观是系统时区,别人(尤其是时间算术函数)是“时区无关”(他们可能会表现得好像时间是UTC)。

亚伦和我怀疑自动生成TIMESTAMP列也打破。 无论2009-11-01 01:30 -04002009-11-01 01:30 -0500将被存储为暧昧2009-11-01 01:30



Answer 2:

我已经想通弄明白了,我的目的。 我总结一下我学会了(不好意思,这些钞票是冗长的,他们尽可能多的为我的未来转诊别的)。

相反的是我在我以前的意见一说,DATETIME和TIMESTAMP领域表现不同。 时间戳字段(如文档显示)采取任何你在“YYYY-MM-DD HH:MM:SS”给他们格式,并从当前时区转换为UTC时间。 当你检索数据的反向透明地发生。 DATETIME字段不进行这种转换。 他们采取任何你给他们,只是直接存储。

无论是DATETIME也不时间戳字段类型可以准确数据存储在该观察DST时区 。 如果存储“2009-11-01 01:30:00”的字段没有办法区分哪些1:30版本是你想要的 - 在-04:00或-05:00版本。

好了,所以我们必须存储在一个非DST时区(如UTC)我们的数据。 时间戳字段无法准确地处理这些数据的原因,我将解释:如果您的系统设置为DST时区那么你投入TIMESTAMP可能不是你所得到的退了出去。 即使你把它,你已经转换为UTC数据时,它仍然会承担数据在您的本地时区,做又转换为UTC。 这TIMESTAMP强制本地到UTC-背到当地的往返是有损耗的,当你的本地时区观察DST(因为“2009-11-01 01:30:00”映射到2周不同的可能的时间)。

随着DATETIME,你可以存储你的数据在任何你想要的时区,并且相信你会回来不管你把它(你不要逼成时间戳字段强加给你的有损往返转换)。 因此,解决办法是使用DATETIME字段并保存到外地之前,将从您的系统时区成你想要将其保存在任何非DST区(我认为UTC可能是最好的选择)。 这允许你建立转换逻辑到你的脚本语言,这样就可以明确地保存UTC相当于“2009-11-01 01:30:00 -04:00”或“” 2009-11-01 01:30: 00 -05:00" 。

需要注意的另一个重要的事情是,MySQL的日期/时间的数学函数不正确周围DST界限,如果你存储在DST TZ日期工作。 因此,更有理由在UTC保存。

概括地说我现在这样做:

当从数据库中检索数据:

明确解释,以获得准确的Unix时间戳从数据库的MySQL UTC之外的数据。 我使用PHP的的strtotime()函数或本公司DateTime类。 它不能可靠内部使用MySQL的CONVERT_TZ()或UNIX_TIMESTAMP()函数,因为CONVERT_TZ将只输出一个“:MM:YYYY-MM-DD HH SS”完成的MySQL从歧义问题遭受值,和UNIX_TIMESTAMP()假定其输入是在系统中的时区,而不是数据实际上存储在(UTC)时区。

当存储数据到数据库:

将您的日期,你的愿望的MySQL之外的精确UTC时间。 例如:用PHP的DateTime类可以指定“2009-11-01 1:30:00 EST”,有别于“2009-11-01 1:30:00 EDT”,然后将其转换为UTC和保存正确的UTC时间您DATETIME领域。

唷。 非常感谢大家的输入和帮助。 希望这样可以节省别人有些头疼的道路。

顺便说一句,我在MySQL 5.0.22和5.0.27看到这



Answer 3:

我认为micahwittman的链接有最好的切实可行的解决方案,以这些MySQL的限制:当您连接设置会话时区为UTC:

SET SESSION time_zone = '+0:00'

然后,你只需把它的Unix时间戳,一切都应该罚款。



Answer 4:

这个线程使我畸形人,因为我们使用TIMESTAMP列用On UPDATE CURRENT_TIMESTAMP (即: recordTimestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP )来跟踪更改的记录和ETL的数据仓库。

万一有人怀疑,在这种情况下, TIMESTAMP的行为正确,你可以通过转换两个类似的日期区分TIMESTAMP到UNIX时间戳:

select TestFact.*, UNIX_TIMESTAMP(recordTimestamp) from TestFact;

id  recordTimestamp         UNIX_TIMESTAMP(recordTimestamp)
1   2012-11-04 01:00:10.0   1352005210
2   2012-11-04 01:00:10.0   1352008810


Answer 5:

但我怎么救什么01:30:00 -04:00?

你可以转换为UTC一样:

SELECT CONVERT_TZ('2009-11-29 01:30:00','-04:00','+00:00');


更妙的是,保存日期为TIMESTAMP场。 这一直保存在UTC和UTC不知道夏天/冬天的时候。

您可以从UTC转换使用到的localtime CONVERT_TZ :

SELECT CONVERT_TZ(UTC_TIMESTAMP(),'+00:00','SYSTEM');

其中“+00:00”是UTC,从该时区,和“SYSTEM”就是MySQL的运行操作系统的本地时区。



Answer 6:

Mysql的固有解决使用TIME_ZONE_NAME表从MySQL数据库这个问题。 使用CONVERT_TZ而CRUD无需担心夏令时更新日期时间。

SELECT
  CONVERT_TZ('2019-04-01 00:00:00','Europe/London','UTC') AS time1,
  CONVERT_TZ('2019-03-01 00:00:00','Europe/London','UTC') AS time2;


Answer 7:

我工作在登录页面的访问次数,并显示在图中的计数(使用海军报jQuery插件)。 我充满了测试数据和一切表中查找罚款,但我注意到,在图的结束点是根据在x轴标签休息一天。 考试结束后,我注意到,对于2015年10月25日当天的观看次数已经从数据库所以每天两次检索并传递到海军报,在此之后日期是一个天向右侧移动。
在我的代码寻找错误一段时间后,我意识到,这个日期是DST发生。 然后,我来到这个页面SO ...
...但建议的解决方案是什么,我需要或他们有其他缺点的矫枉过正。 我不是不能够含糊时间戳之间的区分很担心。 我只需要计算每天显示记录。

首先,我检索日期范围:

SELECT 
    DATE(MIN(created_timestamp)) AS min_date, 
    DATE(MAX(created_timestamp)) AS max_date 
FROM page_display_log
WHERE item_id = :item_id

然后,在for循环中,从min_date ,结尾max_date ,以一天(步骤60*60*24 ),我检索次数:

for( $day = $min_date_timestamp; $day <= $max_date_timestamp; $day += 60 * 60 * 24 ) {
    $query = "
        SELECT COUNT(*) AS count_per_day
        FROM page_display_log
        WHERE 
            item_id = :item_id AND
            ( 
                created_timestamp BETWEEN 
                '" . date( "Y-m-d 00:00:00", $day ) . "' AND
                '" . date( "Y-m-d 23:59:59", $day ) . "'
            )
    ";
    //execute query and do stuff with the result
}

我对我的问题最终快捷的解决方案是这样的:

$min_date_timestamp += 60 * 60 * 2; // To avoid DST problems
for( $day = $min_date_timestamp; $day <= $max_da.....

所以我不盯着在当天开始的循环,但两个小时后 。 这一天还是一样,我仍然获取正确的次数,因为我明确要求00:00:00,一天的23:59:59之间的记录的数据库,无论时间戳的实际时间。 而当时间跳跃一个小时,我仍然在正确的日期。

注:我知道这是5岁的线程,我知道这是不是一个答案OP的问题,但它可能会帮助我这样的人谁遇到这样的页面寻找解决我所描述的问题。



文章来源: MySQL datetime fields and daylight savings time — how do I reference the “extra” hour?