Когда мне нужно завтра, я прибавляю 24 часа. Все знают, что это ломается дважды в год, все произносят слово “часовой пояс” на ревью, а потом правка уезжает в ветку вместе с этими 24 часами, потому что лечится оно библиотекой, которую сегодня никто не хочет тащить. Примерно так у меня и вышло в марте. Чего я не делал ни разу, так это не смотрел, что база говорит про содержимое года.
Поэтому прочитал её целиком. Каждую зону, которую знает рантайм, смещение на каждый день 2011 года. Каждое изменение уточнено до минуты двоичным поиском внутри суток. Получилось 418 зон, из них двигались хотя бы раз 185, всего 344 скачка.
336 скачков из этих 344 это тот самый час, вокруг которого весь спор. Интересны остальные 8.
Остров Лорд-Хау двигается на полчаса. Это единственная такая зона в базе. Антарктические станции двигаются на 2 часа, Тролль дважды, Дэвис один раз. Кейси двигается на 3. Это 6 скачков, которых набор тестов из одних часовых случаев не увидит никогда.
Дальше идёт 30 декабря, которого ещё не было. До него 9 дней.
Сутки, которых не будет
В 10 утра по UTC в этот день Апиа уходит с минус 600 минут на плюс 840. Через час то же делает Факаофо. Обе перешагивают линию перемены даты, поэтому местный календарь идёт с четверга 29 декабря сразу на субботу 31 декабря. Для того, кто стоит там, пятницы не случится.
Любое расписание, которое отсчитывает сутки прибавлением 86400000 миллисекунд, выдаст в этих зонах пятницу. Любой отчёт с группировкой по местной дате получит пустую корзину в середине. Любая разница двух моментов, посчитанная в сутках, окажется на 1 больше.
Проверить это сегодня мне нечем. Никому нечем. Правило лежит в базе с проставленной датой. Это и есть всё предупреждение, которое кто-либо получит.
Весной вперёд, осенью назад, 26 исключений
Число, из-за которого я заподозрил собственный скрипт: 186 скачков идут вперёд и только 158 назад. Время положено возвращать к концу года, поэтому разница в 28 выглядела как ошибка в том, как я обхожу границу года.
Ошибки нет. 30 зон заканчивают 2011 год на смещении, отличном от того, с которого начали.
25 зон из 30 это Россия и Беларусь, которые ушли на летнее время в марте и там остались. Минск в этом списке. Это как раз та часть, которой я не знал, пока её не напечатал скрипт. Весь год я смотрел на эти часы на стене. Двадцать шестая зона с плюс 60 это Баия в Бразилии, у которой просто середина лета, когда год заканчивается.
Что коду приходится переживать
186 скачков вперёд означают 186 отрезков местного времени, которых в 2011 году не существует. Спроси 02:30 не в ту ночь не в той зоне: такого момента нет. 158 скачков назад означают 158 отрезков, которые случаются дважды, поэтому местная отметка времени без смещения двусмысленна на час. На Лорд-Хау она двусмысленна на полчаса.
И сами смещения не те круглые числа, которые обычно подразумевает схема данных. За год база использует 41 разное смещение от UTC. Из них 15 не кратны часу: привычные половины плюс 5.75, 8.75, 12.75 и 13.75.
Чего я не проверял
Говорит ли база, которую я читаю сегодня, правду про январь. Данные зон правят задним числом, поэтому файл, описывающий 2011 год, это файл, который правили в течение 2011 года. Мой обход не отличит правило от его позднейшей поправки. Ещё я прочитал только то, что поставляется с рантаймом, то есть одну копию базы вместо нескольких, которые обычно живут в системе. Значит операционная система, язык и движок базы данных могут отвечать каждый со своей давности. Это то расхождение, на которое я уже напарывался. Здесь оно не измерено.
Узкое утверждение это правило о том, чего не предполагать. Сутки это не 24 часа, смещение это не целое число часов, часы не возвращаются к концу года. А через 9 дней пятница пропадёт у двух зон, у которых есть все основания рассчитывать на пятницу.