DST 结束时的 Laravel Carbon addHour()
Posted
技术标签:
【中文标题】DST 结束时的 Laravel Carbon addHour()【英文标题】:Laravel Carbon addHour() when DST ends 【发布时间】:2019-01-04 18:20:54 【问题描述】:我正在使用在 Ubuntu 服务器上运行的 Laravel 5.5。
服务器时区设置为 EEST。
Laravel 时区设置在 config/app.php
到 'timezone' => 'Europe/Sofia',
这里(欧洲/索非亚)夏令时从 3 月 25 日 03:00 开始,到 10 月 28 日 04:00 结束。
当我运行以下代码时:
$datetime = Carbon::createFromDate(2018, 3, 24);
$datetime->hour = 23;
$datetime->minute = 0;
$datetime->second = 0;
dump($datetime);
foreach (range(1,12) as $v)
$datetime->addHour(1);
echo $datetime.'<br /> ';
我得到了预期的输出(缺少 03:00 小时):
Carbon @1521925200 #505 date: 2018-03-24 23:00:00.0 Europe/Sofia (+02:00)
2018-03-25 00:00:00
2018-03-25 01:00:00
2018-03-25 02:00:00
2018-03-25 04:00:00
2018-03-25 05:00:00
2018-03-25 06:00:00
2018-03-25 07:00:00
2018-03-25 08:00:00
2018-03-25 09:00:00
2018-03-25 10:00:00
2018-03-25 11:00:00
2018-03-25 12:00:00
现在,当我将日期设置为 DST 结束时:
$datetime = Carbon::createFromDate(2018, 10, 27);
$datetime->hour = 23;
$datetime->minute = 0;
$datetime->second = 0;
dump($datetime);
foreach (range(1,12) as $v)
$datetime->addHour(1);
echo $datetime.'<br /> ';
输出是:
Carbon @1540670400 #211 date: 2018-10-27 23:00:00.0 Europe/Sofia (+03:00)
2018-10-28 00:00:00
2018-10-28 01:00:00
2018-10-28 02:00:00
2018-10-28 03:00:00
2018-10-28 04:00:00
2018-10-28 05:00:00
2018-10-28 06:00:00
2018-10-28 07:00:00
2018-10-28 08:00:00
2018-10-28 09:00:00
2018-10-28 10:00:00
2018-10-28 11:00:00
现在我预计第 3 个小时会出现两次。 如果这是 Carbon addHour() 的正确输出,是否有一种在 DST 开始和结束期间获取日期时间的好方法?
【问题讨论】:
【参考方案1】:首先,我们可以在这里丢弃 Carbon 和 Laravel。这一切都发生在 PHP 核心中。我们还可以验证 PHP 安装所具有的时区信息,尽管它不太可能不正确,除非保加利亚最近改变了它的规定。你可能会得到和我一样的转换:
$tz = new DateTimeZone('Europe/Sofia');
$start = new DateTime('2018-01-01', $tz);
$end = new DateTime('2019-01-01', $tz);
var_dump($tz->getTransitions($start->format('U'), $end->format('U')));
array(3)
[0]=>
array(5)
["ts"]=>
int(1514757600)
["time"]=>
string(24) "2017-12-31T22:00:00+0000"
["offset"]=>
int(7200)
["isdst"]=>
bool(false)
["abbr"]=>
string(3) "EET"
[1]=>
array(5)
["ts"]=>
int(1521939600)
["time"]=>
string(24) "2018-03-25T01:00:00+0000"
["offset"]=>
int(10800)
["isdst"]=>
bool(true)
["abbr"]=>
string(4) "EEST"
[2]=>
array(5)
["ts"]=>
int(1540688400)
["time"]=>
string(24) "2018-10-28T01:00:00+0000"
["offset"]=>
int(7200)
["isdst"]=>
bool(false)
["abbr"]=>
string(3) "EET"
简而言之,时区在 EET (+0200) 和 EEST (+0300) 之间交换:
2018-03-25 01:00:00 UTC(当地时间 3:00) 2018-10-28 01:00:00 UTC(当地时间 4:00)故事是 PHP 确实将时区和 DST 考虑在内,但我在实践中发现在跨边界的日期数学方面存在一些微妙的错误。
3 月看起来一切正常:
$datetime = new DateTime('2018-03-25 02:59:59', $tz);
echo $datetime->format('r'), PHP_EOL;
$datetime->modify('+1 second');
echo $datetime->format('r'), PHP_EOL;
echo PHP_EOL;
Sun, 25 Mar 2018 02:59:59 +0200
Sun, 25 Mar 2018 04:00:00 +0300
在 10 月,它会提前一小时进行过渡,您不会看到重复的时间,因为它发生在……:59:59:
$datetime = new DateTime('2018-10-28 02:59:59', $tz);
echo $datetime->format('r'), PHP_EOL;
$datetime->modify('+1 second');
echo $datetime->format('r'), PHP_EOL;
echo PHP_EOL;
Sun, 28 Oct 2018 02:59:59 +0300
Sun, 28 Oct 2018 03:00:00 +0200
但是,如果您使用 UTC 进行数学运算,则会得到不同的结果:
$datetime = new DateTime('2018-10-28 02:59:59', $tz);
echo $datetime->format('r'), PHP_EOL;
$datetime->setTimezone($utc);
$datetime->modify('+1 second');
$datetime->setTimezone($tz);
echo $datetime->format('r'), PHP_EOL;
echo PHP_EOL;
Sun, 28 Oct 2018 02:59:59 +0300
Sun, 28 Oct 2018 03:00:00 +0300
我并没有明确解释为什么会发生这种情况(我不熟悉 PHP 内部),但我怀疑这与 10 月 28 日有两个不同的3:00
local 的事实引起的歧义有关小时,但时区信息 (Europe/Sofia
) 不包含足够的信息来确定您的意思。但是,当您从 UTC 转换时,就没有歧义了:
我的建议是要么使用它,要么尽可能使用 UTC 进行所有内部计算,并切换到本地时间仅用于显示目的。
【讨论】:
以上是关于DST 结束时的 Laravel Carbon addHour()的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Laravel 5 中从 Carbon 获取当前时间戳