在夏令时变化时,可以将文件时间与strtotime进行比较吗?

时间:2016-03-17 22:10:55

标签: php dst

如果源数据文件已过期,我有一些PHP代码可以发送警告并自动关闭服务。这是通过以下比较完成的:

警告: filemtime($file_location) < strtotime('-15 minute')

考虑减少服务: filemtime($file_location) < strtotime('-1 hour')

从夏令时开始时间凌晨3点开始1小时,这会错误地将文件报告为超过15分钟。在一小时结束时,它还错误地将文件报告为超过1小时。凌晨4点,CDT事情恢复正常。

有什么东西我不知道filemtime()或strtotime()函数可以解释这种行为吗?我的理解是,两者都返回UNIX时间戳,并且时间戳是按UTC定义的,所以我不确定是什么原因导致这个问题。

根据目前为止的答案,我在我们的服务器上运行了这个测试:

`

for($i = 1; $i <=4; $i++){
    // let's say current time is this:
    date_default_timezone_set('America/Winnipeg');
    $current_timestamp = strtotime('2016-03-13 0'.$i.':00:00'); // 1457856060

    // DST start at 03:00 that day, so 60 minutes before the time should be 01:01
    $ts = $current_timestamp - 1*60*60;
    echo '-3600s:   ', $ts, ", ", date('Y-m-d H:i:s', $ts), "\n";
    // 1457852460, 2016-10-30 02:59:00, correct

    // now let's test strtotime with -1 hour
    $ts = strtotime('-1 hour', $current_timestamp);
    echo '-1 hour:  ', $ts, ", ", date('Y-m-d H:i:s', $ts), "\n";
    // 1457856060, 2016-10-30 03:01:00, completely wrong

    // DateTime implementation seems smarter:
    $dt = new DateTime();
    $dt->setTimestamp($current_timestamp);
    $dt->sub(new DateInterval('PT1H'));
    echo 'sub PT1H: ', $dt->getTimestamp(), ", ", $dt->format('Y-m-d H:i:s'), "\n";
    // 1457856060, 2016-10-30 03:01:00, correct

    echo "\n\n";
    echo 'TS        ', $current_timestamp, ", ", date('Y-m-d H:i:s', $current_timestamp), "\n";
    echo "\n\n";
}

`

-3600s:1457848800,2016-03-13 00:00:00

-1小时:1457848800,2016-03-13 00:00:00

sub PT1H:1457848800,2016-03-13 00:00:00

TS 1457852400,2016-03-13 01:00:00

-3600s:1457852400,2016-03-13 01:00:00

-1小时:1457856000,2016-03-13 03:00:00

sub PT1H:1457856000,2016-03-13 03:00:00

TS 1457856000,2016-03-13 03:00:00

-3600s:1457852400,2016-03-13 01:00:00

-1小时:1457856000,2016-03-13 03:00:00

sub PT1H:1457856000,2016-03-13 03:00:00

TS 1457856000,2016-03-13 03:00:00

-3600s:1457856000,2016-03-13 03:00:00

-1小时:1457856000,2016-03-13 03:00:00

sub PT1H:1457856000,2016-03-13 03:00:00

TS 1457859600,2016-03-13 04:00:00

2 个答案:

答案 0 :(得分:1)

这令人非常困惑。在我的时区,夏令时从3月27日开始,所有以下表达式都返回相同的UNIX时间戳:

var_dump(strtotime("27 March 2016 02:00"));
var_dump(strtotime("- 60 minute", strtotime("27 March 2016 02:00")));
var_dump(strtotime("- 1 hour", strtotime("27 March 2016 02:00")));
var_dump(strtotime("27 March 2016 02:00 - 1 hour"));
var_dump((new DateTime("27 March 2016 02:00"))->format("U"));
var_dump((new DateTime("27 March 2016 02:00"))->sub(new DateInterval("PT1H"))->format("U"));

如果我对此有足够的考虑,我或许可以相信它是预期的行为。 3月27日01:00 此处不存在。但它肯定不直观。

更令人困惑的是,从02:00减去15分钟会给出的答案,即02:45。即使第一个结果有点意义,如果你眯着眼睛,这个结果必定是一个错误。

关于这一点an open bug report已有近4年的未触动过。

这给你留下了两个选择。您可以在代码中添加if子句,如果初始减法没有减少时间戳,则减去2小时或75分钟。或者您可以简单地从当前时间戳中减去3600或900秒,而不是使用strtotime。两者都不是特别优雅,但我们就是。

答案 1 :(得分:1)

似乎strtotime似乎不够聪明,无法在对相对值进行操作时考虑DST更改:

// let's say current time is this:
date_default_timezone_set('Europe/Berlin');
$current_timestamp = strtotime('2016-10-30 01:59:00'); // 1477785540

// DST ends at 03:00 that day, so 120 minutes later the time should be 02:59
$ts = $current_timestamp + 2*60*60;
echo $ts, ", ", date('Y-m-d H:i:s', $ts), "\n";
// 1477792740, 2016-10-30 02:59:00, correct

// now let's test strtotime with +2 hours
$ts = strtotime('+2 hours', $current_timestamp);
echo $ts, ", ", date('Y-m-d H:i:s', $ts), "\n";
// 1477796340, 2016-10-30 03:59:00, completely wrong

// DateTime implementation seems smarter:
$dt = new DateTime();
$dt->setTimestamp($current_timestamp);
$dt->add(new DateInterval('PT2H'));
echo $dt->getTimestamp(), ", ", $dt->format('Y-m-d H:i:s');
// 1477792740, 2016-10-30 02:59:00, correct

在您的特定情况下,我只是直接比较时间戳(显然差异以秒为单位),但使用DateTimeTimeInterval也是一种选择。