Часовые пояса и чувствительность
1) Базовые принципы
UTC как транспорт и хранение. Все серверные таймстемпы и ключи сортировки — в UTC. Преобразование в локальное «стенное» время — на краю (edge/UI) или в специально выделенном сервисе форматирования.
Зона ≠ смещение. `Europe/Kyiv` — это не просто `UTC+02:00`: правила меняются со временем. Храните идентификаторы IANA (tzdb) в профиле пользователя/объекта, а не «+03:00».
Четкое различение часов.
Wall clock (человеческое время, подвержено DST).
UTC clock (универсальная шкала).
Monotonic clock (для измерения длительностей и таймаутов). Никогда не вычисляйте таймауты на «стенных» часах.
Идempotентность и толерантность к дрожанию времени. Системы должны корректно переживать небольшие скачки NTP/смещения.
2) Модель данных и API-контракты
События: `occurred_at` (UTC, RFC 3339), `timezone` (IANA), опционально `wall_time` (локальный с сохранением смещения при создании).
Периоды: используйте полузакрытые интервалы `[start, end)` в UTC; для человекочитаемых расписаний храните исходное выражение + зону.
Повторяющиеся правила: сериализуйте как RRULE/cron-эквивалент + IANA-зона. Планирование делегируйте движку, который понимает DST.
Формат времени в API: ISO 8601/RFC 3339 с явным `Z` или смещением, например `2025-10-31T17:00:00Z`. Не передавайте «плавающие» строки без смещения.
Версионирование: изменение бизнес-правил времени (например, переход страны на постоянный UTC+1) — это миграция конфигураций и перерасчет расписаний; учитывайте в схемах версий.
3) Летнее время (DST): амбигуитеты и пропуски
Дублированные локальные времена. Осенью локальное «02:30» может случиться дважды. Планировщик в зоне должен различать `2025-10-26T02:30+03` и `2025-10-26T02:30+02`.
Пропущенные локальные времена. Весной «перескакивают» минутные интервалы (например, `02:00–03:00` не существует). Планировщик обязан определить стратегию: перенести на `03:00`, пропустить или выполнить «как можно скорее».
Рекомендация: храните задание как «локальное правило + зона», а фактические инстансы материализуйте в UTC заранее (rolling window), с фиксацией выбранной политики на DST.
4) Leap seconds и time smear
Leap second. Добавочная секунда иногда вставляется в UTC. Большинство бизнес-процессов не должны «видеть» 23:59:60.
Smear (размазывание). Некоторые среды мягко распределяют корректировку на окно (например, ±12 часов), чтобы избежать скачка.
Практика: согласуйте единую политику времени для всего кластера (NTP/смир), логируйте ее в метаданных и держите в runbook.
5) Планировщики и cron-паттерны
Опасность «простого cron». Классический cron не знает DST и IANA-зон. Используйте движки, где расписание привязано к зоне (Quartz-класс, облачные Scheduler-сервисы, Kubernetes CronJob с зоной через контроллер/адмиссион).
Материализация расписаний. Для надежности материализуйте ближайшие N запусков в UTC (например, на 7–30 дней), храните cursor и детерминируйте политику при DST.
Идempotентность задач. Ключ deduplication: `(job_id, scheduled_at_utc)`; повторный запуск не должен дублировать побочные эффекты.
Скольжение часов. При длительных паузах/инцидентах решите, делать ли catch-up (выполнить пропущенное) или skip. Конфигурируйте per-job.
6) Время в протоколах и очередях
Событийные шины (Kafka/Pulsar). Храните `event_time` и `ingest_time` отдельно. Для ретроспективных перерасчетов используйте `event_time`.
Идемпотентные потребители. При повторной доставке ориентируйтесь на ключ события и `event_time`, а не на смещение в партиции.
Сортировка и окна. Окна «за 24 часа по локальному времени магазина» вычисляйте как UTC-интервалы, полученные из локальных правил конкретной зоны на конкретную дату.
7) Логи, трассировки, метрики
Единый таймзонный стандарт: все технические логи и метрики в UTC (с указанием `Z`). Отображение в дашбордах — локализованное для пользователя.
Трассировки: передавайте `trace_start_utc`, `duration_ms` на монотонических часах. Никогда не вычитайте «стенные» таймстемпы.
Бизнес-отчеты: формируйте «сутки» в зоне домена (например, `Europe/Paris` для французского налога), а не по UTC. Четко документируйте.
8) Пользовательские профили и контент
Профиль: `preferred_timezone` (IANA), `preferred_locale`, `currency`, `week_start` (Mon/Sun).
Мультизонные сущности: для команд/организаций храните «зону домена» (например, магазина/юрлица) независимо от персональной зоны участника.
Нотификации: вычисляйте «тихие часы» в зоне пользователя; отправку шедулите из UTC-окна, с безопасностью при DST.
9) Анти-паттерны
Хранить только локальное время без смещения/зоны.
Жестко прошивать смещение `+hh:mm` вместо IANA-идентификатора.
Считать длительности через разность двух «стенных» таймстемпов.
Планировать по cron без поддержки зон/DST.
Делать аналитику «по суткам» в UTC, когда норматив требует локальную зону.
Предполагать неизменность правил зоны (страны меняют политику времени).
10) Тестирование времени
Контролируемые часы. Инъектируйте «часы» в код (Clock/TimeProvider) для детерминированных тестов.
Наборы кейсов:- Переход на летнее/зимнее время (дубли/пропуски).
- Перемещение пользователя между зонами (смена `preferred_timezone`).
- Изменение правил в tzdb (обновление базы — регрессионные тесты).
- Смещения NTP, задержанная доставка событий.
- Фаззи-тесты. Случайные зоны, даты, форматы; сравнение с эталонной библиотекой.
11) Наблюдаемость и эксплуатация
Алерты: рассинхронизация NTP, отставание обновления tzdb, всплески «невыполненных» cron-задач при DST.
Дашборды: распределение событий по зонам/локальным суткам; счетчики catch-up/skip.
Runbook: процедуры при смене правил времени в юрисдикции; порядок обновления tzdb; коммуникация с владельцами расписаний.
12) Паттерны реализации
Time Normalization Gateway. Тонкий сервис, нормализующий входящие времена к RFC 3339 UTC, валидирующий зоны (IANA) и дополняющий контекст.
Local-Day Builder. Библиотека/сервис, который из «локального дня» и зоны строит точные UTC-границы `[start_utc, end_utc)`, учитывая DST.
Schedule Materializer. Планировщик, который хранит правила в виде «локальное выражение + зона», материализует будущие инстансы в UTC и управляет коллизиями/пропусками.
Dual-Timestamp Events. События с полями `occurred_at_utc`, `wall_time_local`, `timezone`. Для UI подставляется локальное, для систем — UTC.
13) Чек-лист архитектора
1. Везде ли хранится UTC?
2. Есть ли у сущностей IANA-зона и дата-политика домена?
3. Планировщик понимает DST и материализует инстансы в UTC?
4. Логи/метрики — в UTC; отчеты — в доменной зоне?
5. Таймауты/ретраи — на монотонических часах?
6. Обновление tzdb автоматизировано и мониторится?
7. Тесты покрывают смену правил, дубли/пропуски минут?
14) Мини-рецепты (псевдокод)
Преобразование локального «рабочего дня» в UTC-интервал
function localDayToUtcInterval(dateLocal, tz):
startLocal = combine(dateLocal, 00:00) in tz endLocal = startLocal + 1 day startUtc = toUTC(startLocal) // учитывает DST endUtc = toUTC(endLocal)
return [startUtc, endUtc)
Материализация повторяющегося расписания
inputs: rrule, tz, windowStartUtc, windowEndUtc for each localOccurrence in expand(rrule, tz, [windowStartUtc, windowEndUtc] projected to tz):
emit occurrence { scheduled_at_utc = toUTC(localOccurrence), tz }
Идeмпотентный ключ запуска задачи
dedupe_key = hash(job_id + scheduled_at_utc.toString())
15) Безопасность и соответствие
Аудит: храните обе проекции времени (UTC и локальную), чтобы согласовать пользовательские претензии («мне обещали до 23:00 по Лиме») с серверной хронологией.
Регуляторика: отчетные периоды формируются в требуемых зонах (налоги, ответственные игровые лимиты, маркетинговые ограничения «по часам»).
Приватность: часовой пояс — персональные настройки, но не точно идентифицирующие данные; обрабатывайте в рамках общих политик конфиденциальности.
Заключение
«Чувствительность ко времени» — это не про формат даты, а про архитектурные границы ответственности: где хранить, где преобразовывать, как планировать и как доказывать корректность. Унификация на уровне UTC, явные IANA-зоны, грамотные планировщики, двойные таймстемпы и монотонические часы превращают время из источника инцидентов в предсказуемый инфраструктурный сервис.
Связанные статьи раздела «Архитектура и Протоколы» (рекомендуется):
- GeoDNS и гео-маршрутизация; Балансировка нагрузки; Наблюдаемость событий во времени; Cron-паттерны и материализация расписаний; Региональные ограничения и локальные отчетные сутки.