Perl DateTime и несуществующий пользовательский ввод времени из-за пересылки часов DST
В сценарии, в котором мы знаем часовой пояс пользователя и дату, но время вводится пользователем в текстовом поле (т. Е. В календаре, в котором пользователь выбирает дату, но вводит время), после того, как оно правильно проанализировано, и мы знаем часы и минуты, как мы должны иметь дело с несуществующим временем из-за перехода часов DST (например, 02:00 не существует из-за того, что часы переведены вперед на 1 час), чтобы передать его - по крайней мере, существующий час - DateTime-> новый ();?
use DateTime;
$dt = DateTime->new(
year => $year_userinput, #2016
month => $month_userinput, #03
day => $day_userinput, #27
hour => $hour_userinput, #02
minute => $minute_userinput, #30
second => 0,
time_zone => $timezone_userinput, #Europe/Berlin
);
Ошибка: неверное местное время для даты в часовом поясе: Европа / Берлин
1 ответ
Когда вы переходите на летнее время, проблем нет. Предполагая, что DLS начинается в 2 часа ночи в данный день в фиктивном часовом поясе, ATZ (часовой пояс) со смещением N часов, затем три секунды, начинающиеся за одну секунду до 2 часов ночи, помечаются меткой времени;
1:59:59 ATZ +N
3:00:00 ADZ +(N+1)
3:00:01 ADZ +(N+1)
... и при выходе из летнего времени...
2:00:00 ADZ +(N+1)
2:00:01 ADZ +(N+1)
...
... about an hour later
...
2:59:59 ADZ +(N+1)
2:00:00 ATZ +N
2:00:01 ATZ
Похоже, что было более раннее "2:00:01", но оно включало смещение (N+1) - или было в ADZ часового пояса - тогда как это в ATZ (N). Модуль DateTime поднимает проблему следующим образом:
Неоднозначное местное время
Из-за перехода на летнее время можно указать неоднозначное местное время. Например, в США в 2003 году переход с сохранения на стандартное время произошел 26 октября в 02:00:00 по местному времени. Местные часы изменились с 01:59:59 (экономия времени) на 01:00:00 (стандартное время). Это означает, что час с 01:00:00 до 01:59:59 фактически происходит дважды, хотя время UTC продолжает двигаться вперед.
Чтобы избежать этой проблемы, вы всегда должны включать часовой пояс или смещение при создании объектов времени. Чтобы сделать это, вам нужно использовать дату (вы сказали, что она у вас есть), чтобы определить, что это день окончания DLS, и, если пользователь выбрал время в течение критического часа, вам придется запросить для "это 2:30 утра ADZ или 2:30 утра ATZ?" или что-то подобное. Аналогично, если это начало DLS, ваш интерфейс должен отклонять записи, ссылающиеся на критический час.
Ранее в документе для DateTime было предложено - по соображениям производительности - один раз определить местный часовой пояс и затем использовать его во всем приложении;
our $App::LocalTZ = DateTime::TimeZone->new( name => 'local' );
... # then everywhere else
my $dt = DateTime->new( ..., time_zone => $App::LocalTZ );
... но это снова сделает вас уязвимым для этой проблемы. Поскольку вашему интерфейсу нужно знать день начала или окончания DLS, вы можете установить и использовать $App::LocalTZ в соответствии с рекомендациями, а затем переопределить конкретный, запрашиваемый часовой пояс, если он закончился. День ДЛС.