碳时区问题

时间:2017-04-05 06:50:55

标签: php datetime timezone php-carbon

date_default_timezone_set()设置的时区与Carbon正在使用的时区不同时,我遇到了Carbon和时区的问题。

在下面的示例中,我有一个while循环,它会添加一个月并恢复到该月的开头,直到$end_date大于$current_date

date_default_timezone_set('Australia/Brisbane');

$tz = new DateTimeZone('Australia/Brisbane');

$start_date = \Carbon\Carbon::instance(new DateTime('2019-03-01 00:00:00', $tz));
$end_date = \Carbon\Carbon::instance(new DateTime('2021-03-21 23:59:00', $tz));

$current_date = $start_date->copy();

while ($end_date->gte($current_date)) {
   echo $current_date->toDateTimeString() . "\n";
   $current_date->addMonth()->startOfMonth();
}

如您所见,输出正确。

2019-03-01 00:00:00
2019-04-01 00:00:00
2019-05-01 00:00:00
2019-06-01 00:00:00
2019-07-01 00:00:00
2019-08-01 00:00:00
2019-09-01 00:00:00
2019-10-01 00:00:00
2019-11-01 00:00:00
2019-12-01 00:00:00
2020-01-01 00:00:00

一旦我将默认时区更改为UTC,我就会得到一个无限循环。为了这个例子,我调整了代码,在10个循环之后停止:

date_default_timezone_set('UTC'); // <---- Changed to UTC

$tz = new DateTimeZone('Australia/Brisbane');

$start_date = \Carbon\Carbon::instance(new DateTime('2019-03-01 00:00:00', $tz));
$end_date = \Carbon\Carbon::instance(new DateTime('2021-03-21 23:59:00', $tz));

$current_date = $start_date->copy();

$x = 0;
while ($end_date->gte($current_date)) {
   echo $current_date->toDateTimeString() . "\n";
   $current_date->addMonth()->startOfMonth();
   $x++;
   if ($x === 10)
       break;
}

这是输出。

2019-03-01 00:00:00
2019-03-01 00:00:00
2019-03-01 00:00:00
2019-03-01 00:00:00
2019-03-01 00:00:00
2019-03-01 00:00:00
2019-03-01 00:00:00
2019-03-01 00:00:00
2019-03-01 00:00:00
2019-03-01 00:00:00

我的期望是,因为我将Australia/Brisbane作为$start_date$end_date的时区,所以此处不应该有任何问题。

最后,如果我重建我的代码以使用DateTime而不是Carbon,我没有问题。

date_default_timezone_set('UTC');

$tz = new DateTimeZone('Australia/Brisbane');

$start_date = new DateTime('2019-03-01 00:00:00', $tz);
$end_date = new DateTime('2020-03-21 23:59:00', $tz);

$current_date = clone $start_date;

while ($current_date->getTimestamp() < $end_date->getTimestamp()) {
    echo $current_date->format('Y-m-d H:i:s') . "\n";
    $current_date->add(new DateInterval('P1M'));
    $current_date->modify('first day of this month');
}

我是否错过了碳处理时区的重要事项?

1 个答案:

答案 0 :(得分:1)

如果您想保留Carbon结构,可以直接使用add函数,如下所示:

$x = 0;
while ($end_date->gte($current_date)) {
   echo $current_date->toDateTimeString() . "\n";
   $current_date->add(new \DateTimeinterval('P1M'))->startOfMonth();
   $x++;
   if ($x === 10)
       break;
}

更新

以下方法是addMonth方法用于将当前日期增加一个月的方法

/**
 * Consider the timezone when modifying the instance.
 *
 * @param string $modify
 *
 * @return static
 */
public function modify($modify)
{
    if ($this->local) {
        return parent::modify($modify);
    }

    $timezone = $this->getTimezone();
    $this->setTimezone('UTC');
    $instance = parent::modify($modify);
    $this->setTimezone($timezone);

    return $instance;
}

正如您所看到的,此函数reset可以在修改日期之前将时区说成UTC然后修改,最后将其转换为给定的时区 - 在此上下文中它是Australia/Brisbane -

实际上,Carbon作者在该函数中将时区重置为UTC的原因尚不清楚,但这是导致问题的原因。