今天在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)。所以有两个问题:
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
答案 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
时所展示的内容。