Почему A - B + B!= A с PHP DateTime и DateInterval?
Может кто-нибудь объяснить мне, почему добавление и вычитание одного и того же DateInterval из объектов DateTime приводит к разным датам? Посмотрите на часы: они идут с 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 ответа
С помощью $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"
}
Недавно у меня возникла похожая проблема с этим фрагментом кода:
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 находится в часовом поясе Европа / Париж. Так, например, при добавлении 7 часов к 2018-03-24 20:00:00 я увеличиваю изменение часового пояса (от +01: 00 до +02: 00), в результате чего сумма на 1 час меньше ожидаемой.
Реализованное решение состоит в том, чтобы инициализировать объект DateTime с часовым поясом UTC, чтобы пропустить проблемы изменения дневного света.
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);
}
Это действительно из-за перехода на летнее время. Для данного времени, если вы, например, измените свой часовой пояс на "America/Los_Angeles", это будет хорошо, потому что в Лос-Анджелесе время изменилось в начале марта. Вы также можете провести тестирование с тем же часовым поясом, указав дату в начале ноября, после чего вы увидите, что за 28 дней до 21:00.
Проверьте здесь для поддерживаемых часовых поясов.