𝗠𝘆 𝗔𝗣𝗜 𝗕𝗿𝗼𝗸𝗲 𝗘𝘃𝗲𝗿𝘆 𝗝𝗮𝗻𝘂𝗮𝗿𝘆 𝟭𝘀𝘁
Мой API сломался ровно в 00:00 UTC 1 января.
Он сломался не в полночь по времени пользователей. Он сломался в полночь по UTC. Пользователи в Токио увидели некорректные данные, начиная с 9 утра по их времени.
Тесты прошли. Стейджинг работал. Ошибка проявлялась только в продакшене, потому что на продакшн-сервере был установлен другой часовой пояс, отличный от стейджинга.
Проблема заключалась в этой логике:
function getDailyReport(date) {
const start = new Date(date).toISOString().split('T')[0];
const end = new Date(start + 'T23:59:59Z');
return db.reports.findMany({
where: {
createdAt: { gte: new Date(start), lt: end }
}
});
}
Когда вы передаете дату вроде "2026-01-01" без указания времени, система использует локальный часовой пояс.
Наш стейджинг-сервер использовал UTC. Наш продакшн-сервер использовал US-East. Это создавало пятичасовое смещение в каждом запросе даты.
Мы теряли пять часов данных в каждом запросе. Цифры были достаточно близки к истине, поэтому никто не замечал этого целый год.
Исправление было простым. Принудительно установите UTC, добавив время к строке:
function getDailyReport(date: string) {
const start = new Date(`${date}T00:00:00Z`);
const end = new Date(`${date}T23:59:59.999Z`);
return db.reports.findMany({
where: {
createdAt: { gte: start, lt: end }
}
});
}
Изменение кода заняло секунды. Исправление системы заняло неделю. Мы сделали следующее:
• Добавили проверку часового пояса в CI, чтобы гарантировать, что среда остается в UTC. • Установили TZ=UTC во всех Dockerfile, чтобы все контейнеры были идентичны. • Добавили проверку часового пояса в скрипт деплоя. • Написали правило для линтера, которое помечает любой конструктор Date без информации о часовом поясе.
Баги с часовыми поясами опасны. Они не обрушивают систему. Они выдают неверные данные, которые выглядят корректно. Ваши пользователи не увидят страницу ошибки. Они увидят неверные цифры и будут им доверять.
Следуйте этим трем правилам:
- Никогда не доверяйте системному часовому поясу. Всегда устанавливайте TZ=UTC.
- Никогда не парсите даты без указания часового пояса.
- Никогда не предполагайте, что часовой пояс в CI совпадает с продакшном.