Molochou
← Все заметки

Таймзоны и дедлайн, который дрейфует

Положите дедлайны на сетку месяца - и вы встретите этот баг. Карточка должна быть готова двенадцатого. Кто-то открывает доску из другой страны, и она стоит на одиннадцатом. Никто ничего не набрал неправильно. Оба экрана показывают правду о разных вопросах.

Две разные вещи, которые называют датой

Момент - это точка на мировой шкале времени. Он один и тот же для всех, а вот называется по-разному в зависимости от того, где вы стоите. Девять вечера в одном месте - следующее утро в другом.

Календарный день - это ярлык, о котором люди договорились локально. «Сдать в пятницу» не означает момент. Означает клетку на сетке, и это одна и та же клетка независимо от того, кто смотрит.

Почти каждая библиотека дат, колонка базы и дефолт API дают первое. Почти каждый дедлайн, который набирает человек, - второе. Баг - в этой конверсии, и он тихий, потому что значение никогда не ошибается сильно. Оно ошибается на одну клетку, на границе, у части ваших пользователей.

Где это реально ломается

Не в хранении. Хранение - легко, когда решено.

Ломается на краях, в местах, где конверсия происходит без чьего-либо выбора. Датапикер, отдающий полночь локального времени, которую сериализуют в UTC. Сервер, форматирующий день для письма своей таймзоной, то есть той, с которой собрали контейнер. Сравнение, спрашивающее, прошёл ли дедлайн, сверкой с «прямо сейчас», когда человек на самом деле спрашивает, прошло ли сегодня за ту клетку.

Каждое из этого - одна строка, каждое по отдельности оправдано, а вместе они дают дедлайн, дрейфующий в зависимости от того, кто открыл доску.

Правило, на котором я останавливаюсь

Если вещь - момент, храните момент: создано, отредактировано, отправлено. Это настоящие точки времени, и конвертировать их для показа - правильное поведение.

Если вещь - день, который выбрал человек, храните день. Голую календарную дату, без времени и без зоны, потому что приложить их - выдумать информацию, которой человек не давал. В «сдать двенадцатого» нет часа, и в момент, когда вы добавляете к нему полночь, вы что-то сочинили, а потом сконвертировали собственное сочинение.

Просрочено тогда значит сравнить сегодняшнюю дату в календаре читателя с тем днём. Два ярлыка, одно сравнение, ничего не сконвертировано.

Звучит занудно ровно до первого раза, когда кто-то пропустит дедлайн, который интерфейс показал ему на день раньше.