PHP DateTime添加在DST上出错 [英] PHP DateTime add gets wrong on DST

查看:47
本文介绍了PHP DateTime添加在DST上出错的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用PHP实现一个带有时间戳和基于时间戳的计算值的表(计算对我的问题无关紧要).

I am implementing a table with a timestamp and a calculated value based on the timestamp (the calculation does not matter for my question) in PHP.

从夏季到冬季(夏令时)的转换过程中,使用add方法时,我注意到PHP的DateTime对象中有一个非常奇怪的行为.

I noticed a very strange behavior in PHP's DateTime object when using the add method at the transistion from summer time to winter time (daylight saving time).

在我的示例中,我将时间戳添加15分钟并进行打印(使用本地格式,unix utc时间戳和以秒为单位的时间戳偏移):

In my example, I am adding 15 minutes to the timestamp and printing it (using local format, unix utc timestamp and timestamp offset in seconds):

<?php

date_default_timezone_set('Europe/Vienna');

$offset = new DateInterval("PT15M");

foreach([new DateTime("2016-03-27 01:00:00"),
         new DateTime("2016-10-30 01:00:00")] as $dt) {
    $lastTs = NULL;

    for($j = 0; $j < 12; $j++) {
        echo $dt->format('d.M.Y H:i:s P (U)');

        if(!is_null($lastTs))
            echo ' (+' . ($dt->format('U') - $lastTs) . ')';

        $lastTs = $dt->format('U');

        echo "\n";

        $dt->add($offset);
    }

    echo "\n";
}

这个小脚本给了我一张这样的表(请注意,2016年10月30日02:15发生了巨大的跳跃):

This little script gives me a table like this (notice the huge jump on 30.Oct.2016 02:15):

27.Mar.2016 01:00:00 +01:00 (1459036800)
27.Mar.2016 01:15:00 +01:00 (1459037700) (+900)
27.Mar.2016 01:30:00 +01:00 (1459038600) (+900)
27.Mar.2016 01:45:00 +01:00 (1459039500) (+900)
27.Mar.2016 03:00:00 +02:00 (1459040400) (+900)
27.Mar.2016 03:15:00 +02:00 (1459041300) (+900)
27.Mar.2016 03:30:00 +02:00 (1459042200) (+900)
27.Mar.2016 03:45:00 +02:00 (1459043100) (+900)
27.Mar.2016 04:00:00 +02:00 (1459044000) (+900)
27.Mar.2016 04:15:00 +02:00 (1459044900) (+900)
27.Mar.2016 04:30:00 +02:00 (1459045800) (+900)
27.Mar.2016 04:45:00 +02:00 (1459046700) (+900)

30.Oct.2016 01:00:00 +02:00 (1477782000)
30.Oct.2016 01:15:00 +02:00 (1477782900) (+900)
30.Oct.2016 01:30:00 +02:00 (1477783800) (+900)
30.Oct.2016 01:45:00 +02:00 (1477784700) (+900)
30.Oct.2016 02:00:00 +02:00 (1477785600) (+900)
30.Oct.2016 02:15:00 +01:00 (1477790100) (+4500)
30.Oct.2016 02:30:00 +01:00 (1477791000) (+900)
30.Oct.2016 02:45:00 +01:00 (1477791900) (+900)
30.Oct.2016 03:00:00 +01:00 (1477792800) (+900)
30.Oct.2016 03:15:00 +01:00 (1477793700) (+900)
30.Oct.2016 03:30:00 +01:00 (1477794600) (+900)
30.Oct.2016 03:45:00 +01:00 (1477795500) (+900)

27月3日,一切看起来都正确.但是当回到冬天的时候,会有一个巨大的飞跃.我不认为这是DST的工作方式.

On 27. Mar everything looks correct. But when going back to winter time, there is a huge jump. I dont think this is how DST works.

相反,我真的很想看看这个输出(在记事本中编辑):

Instead, I really wanted to see this ouput (edited in notepad):

30.Oct.2016 01:45:00 +02:00 (1477784700) (+900)
30.Oct.2016 02:00:00 +02:00 (1477785600) (+900)
30.Oct.2016 02:15:00 +02:00 (1477786500) (+900)
30.Oct.2016 02:30:00 +02:00 (1477787400) (+900)
30.Oct.2016 02:45:00 +02:00 (1477788300) (+900)
30.Oct.2016 02:00:00 +01:00 (1477789200) (+900)
30.Oct.2016 02:15:00 +01:00 (1477789200) (+900)
30.Oct.2016 02:30:00 +01:00 (1477791000) (+900)
30.Oct.2016 02:45:00 +01:00 (1477791900) (+900)
30.Oct.2016 03:00:00 +01:00 (1477792800) (+900)
30.Oct.2016 03:15:00 +01:00 (1477793700) (+900)

现在02:00发生了2次,但偏移量不同(并带有unret时间戳).

Now 02:00 occurs 2 times but with different offsets (and corret unix timestamps).

需要对我的代码进行哪些更改才能获得如上所示的正确结果?

推荐答案

无法使用带有 Europe/Vienna 时区的DateTime对象来实现它.

There is no way to achieve it with DateTime object with Europe/Vienna timezone.

PHP不存储时间戳,而是本地时间+时区查看此答案以获取详细信息.当您执行 $ dt-> format('U')时,它将本地时间转换为时间戳. 2016年10月30日02:15:00(欧洲/维也纳)可以解析为2个时间戳:1477786500(2016年10月30日,星期日,00:15:00 UTC)和1477790100(10月30日,星期日)2016 01:15:00 UTC).由于模棱两可,PHP选择了后者,这打乱了您的计算.

PHP does not store timestamps, but local time + timezone Check this answer for details. When you do $dt->format('U') it converts local time to timestamp. 30.Oct.2016 02:15:00 (Europe/Vienna) can be resolved to 2 timestamps: 1477786500 (Sun, 30 Oct 2016 00:15:00 UTC) and 1477790100 (Sun, 30 Oct 2016 01:15:00 UTC). Since the ambiguity, PHP picks the later one, which breaks your calculations.

解决方法是对任何日期时间操作使用 UTC 时区,并将其转换为仅用于输出的本地时区:

The workaround is to use UTC timezone for any date-time manipulations, and convert it to local timezone only for output:

$utc = new DateTimeZone('utc');
$viena = new DateTimeZone('Europe/Vienna');

$offset = new DateInterval("PT15M");

foreach([new DateTime("2016-03-26 23:00:00", $utc),
         new DateTime("2016-10-29 23:00:00", $utc)] as $dt) {
    $lastTs = NULL;

    for($j = 0; $j < 12; $j++) {
        $local = clone $dt;
        $local->setTimeZone($viena);
        echo $local->format('d.M.Y H:i:s P'); // <== the only place where you need local timezone
        echo $dt->format(' (U)');

        if(!is_null($lastTs))
            echo ' (+' . ($dt->format('U') - $lastTs) . ')';

        $lastTs = $dt->format('U');

        echo "\n";

        $dt->add($offset);
    }

    echo "\n";
}

这篇关于PHP DateTime添加在DST上出错的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆