在我的申请中,我试图计算格林尼治标准时间(英国时间)午夜时间。目前我正在这样做:
$now = new DateTime();
$timeToMidnight = $now->setTimezone(new DateTimeZone('Europe/London'))->diff(new DateTime('tomorrow'))->format('%h hours, %i minutes and %s seconds');
代码正在运行,但似乎落后一小时(使用GMT -1)。目前时间是晚上11:49,输出结果为:
1 hours, 10 minutes and 36 seconds
我已经仔细检查了我的php.ini
,我也将该时区设为GMT:
date.timezone = Europe/London
通过选中phpinfo()
确认了这一点。
是什么给出的?为什么我的应用程序没有使用正确的时区?
答案 0 :(得分:22)
我在Linux PHP 5.5.5上进行了测试,Europe/London
设置为php.ini
中的时区。我实际上将时钟设置为四小时来完成此操作。我用来复制的最小代码是:
$d = new DateTime('tomorrow');
echo $d->format('c e');
(正确)输出是:
2013-10-27T00:00:00+01:00 Europe/London
我将在PHP中查找错误或时区数据中的错误。为了找出哪一个,我们将会看到今晚伦敦其他节目的其他节目。 Epoch Converter告诉我这应该有一个1382828400的Unix时间戳。为了仔细检查时间戳,我用PHP运行:
$d = new DateTime('27-10-2013');
echo $d->format('U');
它还返回1382828400.所以,让我们看看它应该显示什么......
TZ=Europe/London date --date="@1382828400" +%c
输出结果为:
Sun 27 Oct 2013 12:00:00 AM BST
正确!所以tzdata没问题。那么让我们来看看PHP。
我运行了示例代码以及date
命令,并获得了以下输出:
1 hours, 29 minutes and 53 seconds
Sat Oct 26 21:30:07 UTC 2013
Sat Oct 26 22:30:07 BST 2013
这当然是正确的。
我认为在这一点上我们已经排除了tzdata和PHP中的错误,需要查看配置问题和程序员期望。
首先,正如我之前所说,欧洲/伦敦不是UTC,它没有夏令时的概念,因此每年不会改变两次。由于它不会导致此类问题,因此服务器在UTC上运行是最佳做法,无论用户所处的时区如何,以及程序在内部使用UTC然后转换为/从本地时区转换的最佳做法仅显示和用户输入。
我最好的猜测是,运行PHP的服务器实际上设置为使用UTC而不是欧洲/伦敦作为其默认时区。这是我可以重现您的问题的唯一配置。该测试的结果是:
date.timezone = UTC
2 hours, 24 minutes and 36 seconds
Sat Oct 26 21:35:24 UTC 2013
Sat Oct 26 22:35:24 BST 2013
展望未来,您应该在任何可行的情况下使用UTC(以及Unix时间戳),并在处理用户输入时尽早转换为本地时间,并尽可能在显示时间之后转换为本地时间。夏季时间即将结束的像这样的边缘情况可能是一个例外,但您必须格外小心,以确保构造的每个新DateTime
对象在构造时都具有正确的时区集,也要注意他们会有这样的问题。
另请参阅庞大而翔实的Daylight saving time and time zone best practices
最后,为了“修复”你的代码,让我们这样做:
$tz = new DateTimeZone('Europe/London');
$now = new DateTime('now', $tz);
$midnight = new DateTime('tomorrow', $tz);
$timeToMidnight = $now->diff($midnight);
echo $timeToMidnight->format('%h hours, %i minutes and %s seconds');