Сегодня встретил интересную особенность (?) В PHP DateTime::setTimestamp()
поведение. Во время зимних изменений летнего времени, когда 1 час повторяется дважды, PHP конвертирует метку времени, чтобы всегда быть вторым часом. Рассмотрим следующий пример:
<?php
$date = new \DateTime("now", new \DateTimeZone("UTC"));
//2018-10-28T01:30:00 UTC, in London DST happens
$date->setTimestamp(1540686600);
echo $date->getTimestamp() . "\n";
//1540686600
echo $date->format('c') . "\n";
//2018-10-28T00:30:00+00:00
$date->setTimezone(new \DateTimeZone("Europe/London"));
echo $date->getTimestamp() . "\n";
//1540690200
$date->setTimezone(new \DateTimeZone("UTC"));
echo $date->getTimestamp() . "\n";
//1540690200
echo $date->format('c') . "\n";
//2018-10-28T01:30:00+00:00
Смотря на источник PHP пытается преобразовать метку времени в местное время (o_O). Итак, два вопроса:
DateTime
объект? (Я знаю о возможности сохранить все в UTC
и конвертировать в местный часовой пояс только при показе)Найденный возможная ошибка подал в PHP
UPD 2016-01-04 (в UTC):
Сузили проблему до DateTime::getTimestamp()
, Рассмотрим следующий код:
<?php
$date = new \DateTime("now", new \DateTimeZone("UTC"));
//2018-10-28T00:30:00 UTC
$date->setTimestamp(1540686600);
echo $date->format('c') . "\n"; //2018-10-28T00:30:00+00:00
$date->setTimezone(new \DateTimeZone("Europe/London"));
echo $date->format('c') . "\n"; //2018-10-28T01:30:00+01:00
$date->setTimezone(new \DateTimeZone("UTC"));
echo $date->format('c') . "\n"; //2018-10-28T00:30:00+00:00
echo $date->getTimestamp() . "\n"; //1540686600
Здесь метка времени не изменяется, и код работает как ожидалось. Следующий код, который похож и был приведен в оригинальном примере, не работает:
<?php
$date = new \DateTime("now", new \DateTimeZone("UTC"));
//2018-10-28T00:30:00 UTC
$date->setTimestamp(1540686600);
echo $date->format('c') . "\n"; //2018-10-28T00:30:00+00:00
$date->setTimezone(new \DateTimeZone("Europe/London"));
echo $date->format('c') . "\n"; //2018-10-28T01:30:00+01:00
//-------------- the only line that was added ------------------
echo $date->getTimestamp() . "\n"; //1540690200
//-------------- end of added line -----------------------------
$date->setTimezone(new \DateTimeZone("UTC"));
echo $date->format('c') . "\n"; //2018-10-28T01:30:00+00:00
echo $date->getTimestamp() . "\n"; //1540690200
Вы сказали:
Отметка времени всегда указывается в UTC и должна соответствовать местному времени, независимо от часового пояса.
Это утверждение противоречит само себе. Отметки времени указаны в UTC, да, но это не зависит от местного времени или часового пояса.
Другими словами, отметка времени всегда относится к определенному моменту времени. местное время может быть неоднозначным, но отметка времени нет. Если вам нужен первый экземпляр неоднозначного местного времени, укажите временную метку на час раньше.
Тем не менее, ваши данные немного неверны. Отметка времени, которую вы указали, 1540686600
, на самом деле это не 1:30 UTC, а скорее соответствует 2018-10-28T01:30:00+01:00
, Следовательно, это действительно первый Появление 1:30 в день резервного перехода DST. Вторым случаем будет 2018-10-28T01:30:00+00:00
что соответствует 1540690200
,
По поводу вашего второго вопроса:
Есть ли простое решение, чтобы сохранить идентификатор часового пояса и предполагаемую метку времени в объекте DateTime?
Вот уже как DateTime
работает в PHP. Он состоит из внутренней метки времени и соответствующего часового пояса. Это именно то, что вы показываете, когда вы звоните setTimezone
,
Других решений пока нет …