为什么A - B + B!=使用PHP DateTime和DateInterval?

时间:2015-06-17 12:15:52

标签: php datetime dateinterval

有人可以向我解释为什么从DateTime对象添加和减去相同的DateInterval导致不同的日期? 查看小时数:当我减去间隔时,从20:00到19:00,但是当我添加间隔时,它仍然是19:00。

$date = new DateTime("2015-04-21 20:00", new DateTimeZone('Europe/Berlin'));

$days = 28;
$minutes = $days * 24 * 60;

$interval = new DateInterval("PT{$minutes}M");

var_dump($date);
$date->sub($interval);
var_dump($date);
$date->add($interval);
var_dump($date);

结果:

object(DateTime)#1 (3) {
  ["date"]=>
  string(26) "2015-04-21 20:00:00.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(13) "Europe/Berlin"
}
object(DateTime)#1 (3) {
  ["date"]=>
  string(26) "2015-03-24 19:00:00.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(13) "Europe/Berlin"
}
object(DateTime)#1 (3) {
  ["date"]=>
  string(26) "2015-04-21 19:00:00.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(13) "Europe/Berlin"
}

在我看来$date->sub($interval);因为夏令时差异而在20:00到19:00之间改变小时(它与其他日期一样有效,即2015-05-21 20:00),但是$date->add($interval);并不适用DTS。这可能是个错误吗?

3 个答案:

答案 0 :(得分:1)

使用$interval->invert似乎可以解决问题:

$date = new DateTime("2015-04-21 20:00", new DateTimeZone('Europe/Berlin'));

$days = 28;
$minutes = $days * 24 * 60;

$interval = new DateInterval("PT{$minutes}M");
$interval->invert = 1;

var_dump($date);
$date->add($interval);
var_dump($date);
$date->sub($interval);
var_dump($date);

结果是:

object(DateTime)#1 (3) {
  ["date"]=>
  string(26) "2015-04-21 20:00:00.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(13) "Europe/Berlin"
}
object(DateTime)#1 (3) {
  ["date"]=>
  string(26) "2015-03-24 19:00:00.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(13) "Europe/Berlin"
}
object(DateTime)#1 (3) {
  ["date"]=>
  string(26) "2015-04-21 20:00:00.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(13) "Europe/Berlin"
}

答案 1 :(得分:0)

确实是因为夏令时。例如,如果您将时区更改为“America / Los_Angeles”,那么在给定的时间内会很好,因为在洛杉矶,时间在3月初发生了变化。 您也可以使用相同的时区进行测试,将您的日期设置在11月初,然后您将看到28天前的21:00。

检查implementing-pushstate-for-twitter.com支持的时区。

答案 2 :(得分:0)

我最近在这段代码中遇到了类似的问题:

private function convertSecondsToInterval($value)
{
    $dt1 = new \DateTime();
    $dt2 = clone $dt1;
    $dt2->add(new \DateInterval('PT'. $value . 'S'));
    return $dt2->diff($dt1);
}

在2018-03-24到2018-03-25之间的夜晚(从CET到法国CEST的时区变化),我注意到一个奇怪的行为:DateInterval结果比预期的少1小时。这是因为默认情况下,DateTime对象位于Europe / Paris时区。因此,例如在2018-03-24 20:00:00增加7小时时,我将时区变化(+01:00变为+02:00),导致总和比预期少1小时。

实施的解决方案是使用UTC时区初始化DateTime对象以跳过日光变换问题。

private function convertSecondsToInterval($value)
{
    $dt1 = new \DateTime('now', new \DateTimeZone('UTC'));
    $dt2 = clone $dt1;
    $dt2->add(new \DateInterval('PT'. $value . 'S'));

    return $dt2->diff($dt1);
}