День 2039. #Оффтоп
Баг в Течение Часа в Год
Было 8 ноября 2021, я работал сортировщиком ошибок в команде Google Docs. День начался как любой другой. Я заварил кофе и начал просматривать отчёты об ошибках за предыдущий день. Кое-что привлекло моё внимание.
Было необычно много сообщений об ошибках, и все они говорили об одном и том же. Пользователь создал ответ или новый комментарий в документе, но его метка времени на странице говорила, что он был создан «завтра». Быстро выявилась закономерность. Все ошибки появлялись:
- У пользователей в тихоокеанском часовом поясе (PT)
- Между 23:00 и 23:59 (PT) 7 ноября
Я бы посчитал это совпадением, но перевод на зимнее время случился в 2:00 (PT) 7 ноября!
Расследование
Баг меня заинтересовал, поэтому я решил заняться его устранением самостоятельно. Не потребовалось много времени, чтобы прийти к выводу, что ошибка, должно быть, в логике форматирования относительной даты Closure Library, которую использовал Docs. Код начинался с попытки вычислить количество дней между текущим временем и временем ввода через:
1. Получение текущего времени (new Date()).
2. Сброс часов, минут, секунд и миллисекунд объекта до нуля, чтобы получить время начала текущего дня.
3. Вычисление количества миллисекунд между временем начала текущего дня и временем ввода, деления его на количество миллисекунд в дне и округления в меньшую сторону.
Я некоторое время смотрел на код, и тут меня осенило! Введенное время между 23:00 и 23:59 (PT) 7 ноября было в поясе тихоокеанского стандартного времени (PST) после окончания летнего времени, но начало текущего дня (00:00) было в поясе тихоокеанского летнего времени (PDT) до окончания летнего времени. Поэтому в тот день между 00:00 и 23:00 не было 23 часов. Два времени были в двух разных часовых поясах с разницей в час, поэтому между двумя временами было 24 часа (целый день)! А что делал код, когда время ввода на день позже начала текущего дня? Он форматировал время как «завтра»…
Исправление
К счастью, класс Date в JS предоставляет удобный метод getTimezoneOffset(), который возвращает количество минут между часовым поясом объекта Date и часовым поясом UTC. Я использовал его для вычисления разницы. По сути, это удаляло любые различия, возникавшие только из-за изменения летнего/зимнего времени между текущим временем и временем начала текущего дня. Так что теперь количество часов между 00:00 и 23:00 в этот день вычислялось как 23!
Бонусный баг
Эта ошибка фактически распространялась и на время начала летнего времени. В 2021 году летнее время началось в 2:00 (по тихоокеанскому времени) 14 марта. Что же произойдёт, если оставить комментарий в 00:00 15 марта, когда текущее время было 23:00 14 марта?
Можно было бы ожидать, что между 00:00 и 23:00 вечера 14 марта будет 23 часа, но их всего 22. Из-за того, что летнее время начинается в 2:00, что на самом деле 3:00, между временем начала дня 14 и 15 марта всего 23 часа!
И что делает код для этого количества часов? Он вычисляет количество дней между двумя временами как ноль из-за округления в меньшую сторону и форматирует время как «сегодня»…
На самом деле этот баг не проявлялся в Google Docs, поскольку нельзя написать комментарий в будущем, и посмотреть его в прошлом.
Почему 1 час в год только по тихоокеанскому времени?
На самом деле эта ошибка теоретически могла произойти в любом часовом поясе. Возможно, все отчёты об ошибках в тот день были случайно получены от пользователей в тихоокеанском часовом поясе. Или, возможно, есть какой-то другой фактор, о котором я не подумал.
Источник: https://tomeraberba.ch/the-1-hour-per-year-bug
Автор оригинала: Tomer Aberbach
Post #2466
2.4K
- 👍 9
- 👎 2