【问题标题】:Is strtotime() having a bug?strtotime() 有错误吗?
【发布时间】:2016-09-21 16:46:40
【问题描述】:

考虑这段代码,我们要在其中添加或减去一秒:

date_default_timezone_set("Europe/Amsterdam");

$time = 1477789199;
echo $time . '  -  ' . date('r', $time) . "\n";
// 1477789199  -  Sun, 30 Oct 2016 02:59:59 +0200

这是正确的,因为这个时间戳仍然在 DST(夏令时/夏令时)之内。

但是现在让我们在时间戳整数上加一秒,然后退出 DST:

$new = $time + 1;
echo $new . '  -  ' . date('r', $new);
// 1477789200  -  Sun, 30 Oct 2016 02:00:00 +0100

万岁! PHP 看到一秒钟后不再有 DST 并显示正确的时间字符串。

但是如果我们没有在时间戳整数上添加一秒,而是使用strtotime() 来添加一秒:

$new = strtotime('+1 second', $time);
echo $new . '  -  ' . date('r', $new);
// 1477792800  -  Sun, 30 Oct 2016 03:00:00 +0100

哎呀!我们只是提前了一个多小时而不是一秒钟。即使你增加一秒、一小时、一天或一年,你总是会得到额外的一小时。即使您添加多年,您也只会获得额外的一小时,这很奇怪,因为我们每年都会进入和退出夏令时,但无论您添加多少年,您都只会获得额外的一小时

但是一旦我们在 10 月份退出 DST 并减去一秒,一切都会正常...

但话又说回来了。如果我们在 3 月,刚刚进入 DST,我们减去一秒,我们反过来观察到完全相同。


等等,什么?!所以……?

echo strtotime('+ 1 second - 1 second', 1477789199); // echoes 1477792799

哇...


对我来说,这听起来像是一个错误。还是这是“设计使然”?有没有人知道这是否记录在某处,或者是否需要报告?

【问题讨论】:

  • 糟糕。如果我在发布之前检查了 PHP 错误池,我会发现 2012 年报告了类似的问题,但没有跟进:bugs.php.net/bug.php?id=62185
  • 来自docUsing this function for mathematical operations is not advisable. It is better to use DateTime::add() and DateTime::sub() in PHP 5.3 and later, or DateTime::modify() in PHP 5.2.
  • 错误报告考虑版本 5.6.26。你有其他版本吗?
  • @sємsєм 是的,5.3 也是,我现在什至读到有人在 php7 中也有它

标签: php time strtotime


【解决方案1】:

这种行为“有据可查”....在测试中:

请参阅https://bugs.php.net/bug.php?id=30532(它还按预期显示您的预期结果)和相关的测试文件(它断言当前行为是正确的)https://github.com/php/php-src/blob/master/ext/date/tests/bug30532.phpt

<?php date_default_timezone_set("America/New_York");

echo date('Y-m-d H:i:s T', strtotime('2004-10-31 EDT +1 hour'))."\n";
echo date('Y-m-d H:i:s T', strtotime('2004-10-31 EDT +2 hours'))."\n";
echo date('Y-m-d H:i:s T', strtotime('2004-10-31 EDT +3 hours'))."\n";
/* 2004-10-31 01:00:00 EDT
   2004-10-31 01:00:00 EST
   2004-10-31 02:00:00 EST */

echo date('Y-m-d H:i:s T', strtotime('2004-10-31 +1 hour'))."\n";
echo date('Y-m-d H:i:s T', strtotime('2004-10-31 +2 hours'))."\n";
echo date('Y-m-d H:i:s T', strtotime('2004-10-31 +3 hours'))."\n";
/* 2004-10-31 01:00:00 EDT
   2004-10-31 02:00:00 EST
   2004-10-31 03:00:00 EST */

请注意,在前一种情况下,时区(此处为:EDT)直接传递给字符串,而在后一种情况下则不是。

一般strtotime 采用时间戳(即2004-10-31 - 或在您的特定情况下:传递的时间戳),转换为具有单个参数的表示,忽略 DST(即单个小时、分钟、秒、天, 月, 年等), 操作被应用到它然后转换回时间戳。

特别是:

echo date('r', strtotime('+ 0 second', 1477789199));
#> Sun, 30 Oct 2016 02:59:59 +0100

strtotime() 在转换后抛出时区,即只需要

Sun, 30 Oct 2016 02:59:59

然后将适用的主要时区应用于您的时区位置(即Europe/Amsterdam),以CET(主要!)结尾 - CEST 也是可能的,但只能是第二选择。

现在,回顾一下上面的测试,只需明确指定原始时区。

因此,如果您希望它按照您需要的方式运行:

echo date('r', strtotime('CEST', 1477789199));
#> Sun, 30 Oct 2016 02:59:59 +0200
echo date('r', strtotime('CEST + 1 second', 1477789199));
#> Sun, 30 Oct 2016 02:00:00 +0100

事实上,添加'CEST ' 在任何时候都可以(因为如果CEST 不匹配并且CET -> CEST 转换没有重叠,它将始终回退到CET)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-05
    • 1970-01-01
    • 2015-03-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多