YourWorldTime

Смещения от UTC, не кратные целому часу

· 4 мин чтения

Примерно пятая часть мира живёт на смещении, не кратном целому часу от UTC. Программа, предполагающая иное, ошибается для этих людей не слегка, а на 30 или 45 минут, что хуже.

Мысленная схема из двадцати четырёх часовых поясов ошибочна дорогостоящим образом: она порождает код, уверенно промахивающийся на 30 или 45 минут для намного более чем миллиарда человек. Ошибка в целый час хотя бы выглядит ошибкой. Ошибка в 45 минут выглядит опечаткой — и уходит в выпуск.

Полный список

Смещения на :30

Смещение Где
UTC-09:30 Маркизские острова
UTC-03:30 Ньюфаундленд и Лабрадор
UTC+03:30 Иран
UTC+04:30 Афганистан
UTC+05:30 Индия, Шри-Ланка
UTC+06:30 Мьянма, Кокосовые острова
UTC+09:30 Центральная Австралия — Северная территория, Южная Австралия
UTC+10:30 Остров Лорд-Хау (стандартное время)

Смещения на :45

Смещение Где
UTC+05:45 Непал
UTC+08:45 Юго-восток Западной Австралии (Юкла, неофициально)
UTC+12:45 Острова Чатем (стандартное время)

Одна лишь Индия — это 1,4 миллиарда человек. Прибавьте Иран, Афганистан, Мьянму, Шри-Ланку и Непал, и вы уже за пятой частью планеты, ещё не считая Австралию или Канаду.

Другой вид необычного смещения

Смещения, не кратные целому часу, — известная странность. Ещё две значат не меньше.

Диапазон равен 26 часам, а не 24. Киритимати в Кирибати живёт по UTC+14; острова Бейкер и Хауленд — по UTC-12. Это 26 часов, а значит, бывают мгновения, когда где-то на Земле одновременно действуют три разные календарные даты. Всякий код, полагающий, что двухдневное окно покрывает все пояса, ошибается дважды в сутки.

Летнее время не всегда равно часу. Остров Лорд-Хау, небольшой австралийский остров с постоянным населением около 380 человек, сдвигается на 30 минут: UTC+10:30 зимой, UTC+11:00 летом. Это единственное место на Земле, где так поступают, и лучший проверочный случай для библиотеки дат: всякая реализация, жёстко задающая шестидесятиминутный сдвиг, ошибается там и больше нигде, а значит, пройдёт любой другой написанный вами тест.

Что ломается

Округление до ближайшего часа. Самый частый отказ. Код, вычисляющий смещение в целых часах, молча урезает Индию до +5 — это 30 минут ошибки для каждого пользователя в стране.

Хранение смещений целыми числами. Столбец схемы, объявленный как счёт часов, не в состоянии представить Катманду. Обнаруживается это обычно уже после того, как данные туда легли.

Сеточные интерфейсы, построенные на часовых столбцах. Всякий календарь или планировщик, раскладывающий сутки на 24 равные клетки и полагающий, что местный час каждого пояса ложится на границу клетки, расставит получасовые и 45-минутные пояса неверно. Полоса в планировщике встреч на этом сайте берёт столбцами мгновения, а не местные часы, поэтому строка +05:45 показывает 09:45 в том столбце, где строка UTC показывает 04:00, — и это правда, а не более опрятная ложь.

Арифметика длительностей через получасовой перевод. Предположение, что переход на летнее время равен ровно 3 600 000 миллисекундам, даёт неверный ответ на острове Лорд-Хау — и только там.

Как сделать правильно

Исправление то же, что чинит почти любую ошибку с часовыми поясами: никогда не вычисляйте смещение сами. Спросите платформу.

Всякая современная среда выполнения несёт базу IANA и назовёт вам смещение пояса в заданное мгновение — в минутах и верно, в том числе для Катманду и Лорд-Хау. В JavaScript это Intl.DateTimeFormat с timeZoneName: 'longOffset' либо сравнение показания часов пояса с UTC — путь, которым идёт движок этого сайта, потому что он остаётся точным даже для секундных смещений местного среднего времени в данных до 1900 года.

Затем держитесь трёх правил:

  1. Смещения — это минуты, никогда не часы. Если переменная зовётся offsetHours, это уже ошибка.
  2. Проверяйте на Катманду и Лорд-Хау. Они рушат те два допущения, которые всё остальное позволяет сохранить.
  3. Никогда не округляйте часовой пояс. Если показ вынужден быть приблизительным, скажите об этом; не двигайте молча часы пользователя на 30 минут ради удобства вёрстки.

Разобранный пример: что 45 минут делают с сеткой

Возьмите планировщик встреч, раскладывающий сутки на 24 столбца, по одному на час, и затеняющий рабочие часы каждого участника. Наивная реализация вычисляет каждую строку, сдвигая опорную строку на смещение пояса, выраженное в часах.

Для Лондона против Нью-Йорка это работает: строка Нью-Йорка — это строка Лондона, сдвинутая на пять клеток вправо. Для Лондона против Катманду не работает. Катманду впереди на 5 часов 45 минут, то есть на 5,75 клетки, а трёх четвертей клетки не бывает. Реализация округляет — обычно до 6 — и теперь каждое показание в этой строке на 15 минут раньше должного.

Ошибка достаточно мала, чтобы пережить обзор кода, и достаточно велика, чтобы иметь значение: встреча на 09:00, которую сетка объявляет девятью часами для Катманду, там на самом деле в 08:45, и присоединяющийся опаздывает на четверть часа к собственному дню.

Исправление — перестать считать столбцы часами. Сделайте каждый столбец мгновением и спросите платформу, что показывают местные часы каждого пояса в это мгновение. Строка +05:45 тогда покажет 09:45 там, где строка UTC показывает 04:00, — явно не по сетке, и это честная отрисовка. Планировщик встреч на этом сайте именно так и делает, а рваный край строки Катманду — достоинство, а не изъян.

Исторические смещения ещё диковиннее

До стандартизации смещения были местным средним солнечным временем и точны до секунды. База IANA их хранит.

Нидерланды с 1909 по 1937 год жили на UTC+00:19:32,13. Либерия до 1972 года была на UTC-00:44:30. Спросите среду выполнения о смещении Europe/Amsterdam на дату 1920 года — и получите точное значение, вплоть до секунд.

Поэтому функция смещения, лежащая под этим сайтом, считает в секундах и округляет до минут только на уровне отображения. Движок, хранящий минуты, тихо потерял бы 32 секунды голландской истории — чего никто бы не заметил и что всё равно было бы неверно.