Британская Колумбия отменила зимнее время, и Postgres 'ломает' дату будущих встреч

Британская Колумбия отменила зимнее время, и Postgres 'ломает' дату будущих встреч

Проблема возникает потому, что timestamptz в Postgres хранит UTC, а не локальное время. Если встреча была заказана как '10:00 Vancouver' до обновления tzdata, она конвертируется в UTC по старым правилам (UTC-8). После обновления tzdata система конвертирует эту UTC обратно в локальное время по новым правилам (UTC-7), и встреча приходит на 11:00 вместо 10:00. Проблема серьёзна: 5,8 млн жителей BC, и если приложение хранит будущие события (appointments, deadlines, calendar), данные расходятся с ожиданиями пользователя. Решение: dual-column pattern, хранить локальное время + timezone name + вычисленное UTC через триггер. При изменении tzdata требуется переобработка будущих событий.

Ключевые факты

  • 8 марта 2026: BC перешла на постоянное UTC-7 (летнее время)
  • Проблема: timestamptz хранит UTC, конвертирует туда-обратно по tzdata при вставке/извлечении
  • Если tzdata обновлена, встречи на ноябрь-март 2026+ будут на 1 час позже, чем пользователь бронировал
  • Решение: dual-column pattern: local_time (timestamp) + timezone_name (text) + starts_at_utc (вычисляется триггером)
  • Требуется UPDATE всех будущих встреч при изменении tzdata для перепересчёта UTC по новым правилам

Почему это важно

Время, это не просто число. Для встреч врачей, доставок, деловых звонков локальное время, это то, что пользователь записал в календарь. Если система тайно переводит это время при изменении политики часовых поясов, возникают кризисы и потеря доверия. Проблема касается не только BC, любой регион может изменить правила DST. tzdata обновляется каждые несколько месяцев.

Кому это важно

Разработчикам приложений с календарём, booking-системами, медицинскими платформами; DBA, отвечающим за Postgres; компаниям, работающим во многих часовых поясах (особенно с DST).

Как это применить

  1. Если цена в прошлом или UTC авторитетен (логи, финтранзакции), используйте timestamptz как есть. 2. Если локальное время авторитетно (встречи, deadlines, события календаря), используйте dual-column pattern. 3. Создайте триггер, который при вставке/обновлении пересчитывает starts_at_utc из local_time AT TIME ZONE timezone_name. 4. При обновлении tzdata запустите UPDATE для перепересчёта будущих встреч. 5. Добавьте UI-уведомление о сдвиге времени встреч.

Можно ли доверять

Статья от Crunchy Data (компании со специалистами по Postgres), содержит валидный SQL-код и примеры. Проблема реальна и документирована в пост-сообществе (ссылка на обсуждение postgresql-general от ноября 2025). Требования RFC 9557 (новый стандарт временных меток) специально исключают future-time сценарии как неразрешимые стандартом.

Риски и подводные камни

Миграция старых данных сложна: нужно точно определить, когда tzdata обновлена, найти затронутые строки, уведомить пользователей, провести тест на staging. Если пользователь уже адаптировался к сдвинутому времени, переобработка может их запутать. Generated columns в Postgres НЕ поддерживают timestamptz (не объявлен как immutable), поэтому нужен триггер, а не встроенная генерация.

«If you stored timestamps in a UTC-based column for British Columbia-based appointment in 2026 and beyond, your November through March appointments may be off by an hour!»

— Crunchy Data, о последствиях tzdata update