Три часовых пояса в одном звонке
Звонок в отчёте на несколько часов раньше или позже, чем был на самом деле.
phpmysql
Отчёт по звонкам показывает разговор в четыре утра. Разговор был днём. На другом стенде тот же звонок отображается верно.
Причина
В одном звонке участвуют минимум три пояса:
- провайдер отдаёт метки в UTC — так делают почти все;
- сервер приложения живёт в своём поясе, и функции вроде
now()возвращают локальное время; - человек, который смотрит отчёт, находится в третьем.
Ошибка возникает там, где метку сохранили как есть, не обозначив пояс. Дальше её читают уже с другим предположением, и разница въезжает в данные молча: исключения нет, просто число другое.
Отдельно подставляет база: соединение может иметь собственный часовой пояс, отличный от системного. Одна и та же вставка через разные подключения ляжет по-разному.
Решение
Правило простое и работает всегда:
Храним в UTC. Конвертируем только на выводе. Метку без пояса не принимаем.
По шагам:
Приём. Разбирать метку провайдера с явным поясом, а не полагаться на дефолт
среды. Если провайдер прислал Z или смещение — использовать его.
Хранение. В UTC, в поле, которое не пересчитывается автоматически. Часовой пояс соединения с базой задавать явно при подключении, а не наследовать от сервера.
Вывод. Переводить в пояс того, кто смотрит. Для отчётов, которые уходят заказчику, пояс лучше писать прямо рядом с временем — тогда расхождение видно сразу, а не через месяц в претензии.
Где это ломается чаще всего
Границы суток. Отчёт «за вчера» при разнице в несколько часов захватывает кусок позавчера или теряет вечер. Проверять фичу нужно именно на границе, а не в середине дня — в середине всё сходится при любой ошибке.
Переход на летнее время. Там, где он есть, смещение меняется дважды в год. Хранение в UTC это переживает, хранение в локальном — нет: час либо повторяется, либо исчезает.
Как убедиться, что починено
Взять звонок и сравнить три значения: что показал провайдер в своей панели, что лежит в базе, что видно в отчёте. Первое и третье должны совпадать после приведения к одному поясу, а в базе должен быть UTC.
Если совпадает — можно спать спокойно. До перехода на летнее время.