Мы привыкли, что в сутках 24 часа. Но дважды в год это неправда: при переходе на летнее и на зимнее время один день длится 23 часа, другой - 25. Один час либо повторяется дважды, либо пропадает совсем. Мелочь, из-за которой ломается на удивление много.
Что из-за этого едет
- Логика "прошло ровно 24 часа" в такой день ошибается на час.
- Задача по расписанию, назначенная на время внутри пропавшего часа, не запускается вообще; назначенная на повторяющийся час - запускается дважды.
- Расчёты "сколько отработано за сутки", "конец дня", "на сколько сдвинуть" - всё тихо уезжает.
Это не теория. На одном живом проекте мы на такой сдвиг и налетели - ровно на разнице между "24 часа" и "одни календарные сутки". С тех пор относимся ко времени с уважением.
Почему выручает UTC
UTC - это единые всемирные часы, у которых нет перевода на летнее время. В UTC сутки всегда ровно 24 часа, час никогда не повторяется и не пропадает. Поэтому всё время внутри системы - в базе, в расчётах, в сравнениях - мы храним и считаем в UTC. Там арифметика времени честная и предсказуемая.
Есть и вторая причина. Местное время - вещь политическая: страны меняют правила перевода часов, сдвигают пояса, вводят и отменяют летнее время. Если хранить "10:00 по местному", через год уже нельзя точно сказать, какому мировому моменту это соответствовало. UTC не меняется никогда - это надёжная точка отсчёта, к которой всегда можно привязаться.
Часовой пояс - только на экране
Пользователю, конечно, нужно видеть своё местное время, а не UTC. Поэтому перевод в часовой пояс мы делаем в самый последний момент - при выводе на экран. Внутри всё живёт в UTC, а "10:00 по Киеву" появляется только там, где на время смотрит человек.
Храни и считай момент времени в UTC, переводи в часовой пояс только при выводе. Тогда странность с 23 и 25 часами остаётся на самом краю системы, где ей и место.
Мелочь, которая избавляет от целого класса плавающих багов "то на час, то не в тот день".

