【问题标题】:Laravel Carbon addHour() when DST endsDST 结束时的 Laravel Carbon addHour()
【发布时间】: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 开始和结束期间获取日期时间的好方法?

【问题讨论】:

    标签: php laravel dst php-carbon


    【解决方案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 转换时,就没有歧义了:

    • 00:59:59 UTC 映射到 EET
    • 01:00:00 UTC 映射到 EEST

    我的建议是要么使用它,要么尽可能使用 UTC 进行所有内部计算,并切换到本地时间仅用于显示目的。

    【讨论】:

      猜你喜欢
      • 2016-08-11
      • 1970-01-01
      • 2013-09-29
      • 2020-12-14
      • 2018-01-10
      • 2020-10-29
      • 2021-11-05
      • 2018-02-24
      • 2015-10-01
      相关资源
      最近更新 更多