在DST冬季变化期间,DateTime会更改时间戳

时间:2015-12-30 16:48:59

标签: php dst

今天在PHP DateTime::setTimestamp()行为中遇到了有趣的功能(?)。在冬季DST更改期间,当1小时重复两次时,PHP会将时间戳转换为始终为第二个小时。请考虑以下示例:

<?php

$date = new \DateTime("now", new \DateTimeZone("UTC"));
//2018-10-28T01:30:00 UTC, in London DST happens
$date->setTimestamp(1540686600);

echo $date->getTimestamp() . "\n";
//1540686600
echo $date->format('c') . "\n";
//2018-10-28T00:30:00+00:00

$date->setTimezone(new \DateTimeZone("Europe/London"));

echo $date->getTimestamp() . "\n";
//1540690200

$date->setTimezone(new \DateTimeZone("UTC"));

echo $date->getTimestamp() . "\n";
//1540690200

echo $date->format('c') . "\n";
//2018-10-28T01:30:00+00:00

查看source PHP尝试将时间戳转换为本地时间(o_O)。所以有两个问题:

  • 为什么会有时间戳的转换?时间戳始终为UTC,应该是本地时间/时区不可知
  • 是否有任何简单的解决方案可以在DateTime对象中保留时区ID和预期时间戳? (我知道有可能将所有内容保留在UTC并仅在显示时转换为本地时区)

在PHP中提交possible bug

UPD 2016-01-04(UTC)

将问题缩小到DateTime::getTimestamp()。请考虑以下代码:

<?php

$date = new \DateTime("now", new \DateTimeZone("UTC"));
//2018-10-28T00:30:00 UTC
$date->setTimestamp(1540686600);
echo $date->format('c') . "\n"; //2018-10-28T00:30:00+00:00

$date->setTimezone(new \DateTimeZone("Europe/London"));
echo $date->format('c') . "\n"; //2018-10-28T01:30:00+01:00

$date->setTimezone(new \DateTimeZone("UTC"));
echo $date->format('c') . "\n"; //2018-10-28T00:30:00+00:00
echo $date->getTimestamp() . "\n"; //1540686600

此处时间戳未被更改,代码按预期工作。下面的代码类似并在原始示例中给出的代码被破坏了:

<?php

$date = new \DateTime("now", new \DateTimeZone("UTC"));
//2018-10-28T00:30:00 UTC
$date->setTimestamp(1540686600);
echo $date->format('c') . "\n"; //2018-10-28T00:30:00+00:00

$date->setTimezone(new \DateTimeZone("Europe/London"));
echo $date->format('c') . "\n"; //2018-10-28T01:30:00+01:00
//-------------- the only line that was added ------------------
echo $date->getTimestamp() . "\n"; //1540690200
//-------------- end of added line -----------------------------

$date->setTimezone(new \DateTimeZone("UTC"));
echo $date->format('c') . "\n"; //2018-10-28T01:30:00+00:00
echo $date->getTimestamp() . "\n"; //1540690200

1 个答案:

答案 0 :(得分:3)

你说:

  

时间戳始终为UTC,应为本地时间,时区不可知

该陈述与自身相冲突。时间戳是UTC,是的,但这不是本地时间或时区不可知。

换句话说,时间戳总是指特定时刻。 本地时间可能不明确,但时间戳不是。如果您想要第一个不明确的本地时间实例,那么请指定一小时前的时间戳。

但是,您的数据略有不正确。您指定的时间戳1540686600实际上不是1:30 UTC,而是对应2018-10-28T01:30:00+01:00。因此,确实是在DST过渡期间1:30的第一次出现。第二次出现是2018-10-28T01:30:00+00:00,对应1540690200

关于你的第二个问题:

  

是否有任何简单的解决方案可以在DateTime对象中保留时区ID和预期时间戳?

这已经是DateTime在PHP中的工作方式了。它由内部时间戳和相关时区组成。这正是您在致电setTimezone时所展示的内容。