Временные зоны — это ложь (и как с ними работать в коде)
Временные зоны на карте кажутся простыми. На самом деле это не так. Вот почему они нарушают программное обеспечение — и правильная модель мышления для их обработки в коде.
Вы открываете карту. Вы видите вертикальные сечения. «UTC-5 — Восточное время, UTC+1 — Центральное европейское время». Просто, верно?
Неверно. Часовые пояса — это политическое создание, а не географическая реальность — и этот разрыв спасёт вас от многих самых ужасных ошибок в разработке программного обеспечения.
Почему часовые пояса на самом деле — это хаос
Вот что делает ваша операционная система с базой tz на вашей стороне:
- Дневное экономление времени (DST) — Не все страны его соблюдают, страны меняют правила, и дата переключения зависит от того, где вы находитесь. В США дата переключения изменилась в 2007 году. Египет отменил DST в 2011 году, затем вновь ввёл его, а затем отменил снова.
- Полу- и четвертьчасовые сдвиги — Индия (UTC+5:30), Непал (UTC+5:45) и части Австралии (UTC+9:30) существуют. Ваше предположение, что сдвиги — целые часы, неверно.
- Исторические изменения — Россия перешла с UTC+4 на UTC+3 в 2014 году. Самоа пропустила целый день (30 декабря 2011 года), чтобы перейти с одной стороны международной линии даты на другую. Когда вы работаете с историческими метками времени, правильный сдвиг может быть совершенно иным, чем сегодня.
- Неопределённые времена — Когда часы уходят вперёд, 1:30 утра происходит дважды. Когда часы возвращаются, 2:30 утра вообще не существует. Если вы храните «3 ноября 2024 года в 1:45 утра Восточного времени» без UTC-сдвига, вы получаете неопределённую метку времени.
Всё это не тривиально. Каждый из этих крайних случаев вызвал реальные ошибки в рабочей среде — пропущенные календарные события, двойные платежи, разрушенные системы планирования.
Одно правило, которое решает большинство проблем
Храните и обрабатывайте в UTC. Преобразовывайте в локальное время только при отображении.
Это не спорное предложение. Это общее мнение всех основных баз данных, стандартов API и команд распределённых систем. Если столбец вашей базы данных хранит 2024-11-03 06:45:00 в UTC, вы всегда точно знаете, какое время это представляет — независимо от того, где размещён ваш сервер, в каком часовом поясе находится ваш пользователь или произошло ли DST в этот день.
Момент, когда вы храните 2024-11-03 01:45:00 без UTC-сдвига, вы теряете информацию. Вы создаёте метку времени, которая может означать два разных момента.
Как обрабатывать часовые пояса в JavaScript
Встроенная в JavaScript Date объект хранит время как миллисекунды с начала эпохи Unix (1 января 1970 года UTC) — что хорошо. Проблема в том, что его API неоднозначен и несогласован.
Что избегать
// BAD: Parsing without explicit timezone
const d = new Date('2024-11-03 01:45:00');
// Interpreted as LOCAL time — behavior varies by environment
// BAD: Storing user-facing strings as dates
const meeting = '3:00 PM Thursday';
// This is meaningless without a timezone
Что использовать вместо этого
// GOOD: Always use ISO 8601 with explicit UTC offset
const d = new Date('2024-11-03T06:45:00Z'); // Z = UTC
// GOOD: Display in user's local timezone using Intl API
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'America/New_York',
dateStyle: 'full',
timeStyle: 'short',
});
console.log(formatter.format(d)); // "Sunday, November 3, 2024 at 1:45 AM"
Если вы создаёте что-то более сложное, чем простая форматировка дат, используйте — ещё один надёжный вариант. Moment.js — полностью функциональный, но больше не поддерживается — перейдите от него. библиотеку. Она имеет первоклассную поддержку часовых поясов, корректно обрабатывает переходы DST и делает намерение кода очевидным.
import { DateTime } from 'luxon';
// Parse an ISO string and convert to a specific zone
const utc = DateTime.fromISO('2024-11-03T06:45:00Z');
const eastern = utc.setZone('America/New_York');
console.log(eastern.toLocaleString(DateTime.DATETIME_FULL));
// "November 3, 2024, 1:45 AM EDT"
Как обрабатывать часовые пояса в Python
Python’s datetime модуль различает между наивными (без часового пояса) и осознанными (с часовым поясом) объектами даты. Наивные даты — это ловушка — избегайте их в любом коде, который пересекает границы часовых поясов.
from datetime import datetime, timezone
import zoneinfo # Python 3.9+
# BAD: naive datetime (no timezone info)
d = datetime(2024, 11, 3, 1, 45, 0)
# GOOD: always attach a timezone
d_utc = datetime(2024, 11, 3, 6, 45, 0, tzinfo=timezone.utc)
# Convert to a specific timezone
eastern = zoneinfo.ZoneInfo('America/New_York')
d_eastern = d_utc.astimezone(eastern)
print(d_eastern) # 2024-11-03 01:45:00-05:00
Для более старых версий Python или сложных задач расписания, python-dateutil и Стрелка — это хорошо поддерживаемые варианты. Основная идея остаётся той же: работайте в UTC, преобразовывайте на границе.
Как обрабатывать часовые пояса в базах данных
Работа с часовыми поясами в базах данных поражает даже опытных разработчиков.
PostgreSQL
PostgreSQL имеет два типа временных меток: timestamp (без часового пояса) и timestamptz (с часовым поясом). Несмотря на название, timestamptz не хранит на самом деле часовой пояс — он преобразует в UTC при записи и возвращает в сессионный часовой пояс при чтении. Это правильное поведение. Всегда используйте timestamptz.
-- BAD
CREATE TABLE events (created_at TIMESTAMP);
-- GOOD
CREATE TABLE events (created_at TIMESTAMPTZ);
-- Querying across timezones
SELECT created_at AT TIME ZONE 'America/New_York' FROM events;
MySQL
MySQL’s DATETIME с нулевым контекстом часового пояса — то, что вы вводите, то и получаете, без преобразований. TIMESTAMP преобразует в UTC при записи и возвращает в текущий серверский часовой пояс при чтении — но ограничивается датами от 1970 до 2038 года (переполнение Unix-времени). На практике: используйте DATETIME столбцы и убедитесь, что ваша приложение всегда записывает значения в UTC.
Планирование по часовым поясам: скрытая ловушка
Представим, что пользователь планирует еженедельное совещание на «каждое понедельник в 9 утра по времени Нью-Йорка». Вы храните следующее наступление как метку времени в UTC. Хорошо — пока не меняются DST, и теперь метка времени в UTC, которую вы хранили, соответствует 10 утра по времени Нью-Йорка вместо 9 утра.
Решение: не храните абсолютные будущие метки времени для повторяющихся событий. Храните правило локального времени — идентификатор часового пояса плюс локальное время — и вычисляйте следующую метку времени в UTC на уровне выполнения. Таким образом, система корректно пересчитывает после переходов DST.
-- Store the intent, not the absolute moment
CREATE TABLE recurring_events (
id SERIAL PRIMARY KEY,
timezone TEXT NOT NULL, -- 'America/New_York'
local_time TIME NOT NULL, -- '09:00:00'
recurrence TEXT NOT NULL -- 'WEEKLY:MONDAY'
);
-- Compute next_occurrence_utc in application code at dispatch time
Имена часовых поясов IANA против UTC-сдвигов
Когда вы храните предпочтение пользователя по часовому поясу, храните имя IANA (America/New_York, Europe/London, Asia/Kolkata) — а не UTC-сдвиг (UTC-5, UTC+1). Сдвиги — это снимки; имена IANA кодируют полную историю DST и будущие правила.
America/New_York является UTC-5 в зимний период и UTC-4 в летний период. Если вы храните UTC-5, вы встраиваете неправильный сдвиг в половину года.
Быстрый справочник: правила
- Храните метки времени в UTC — всегда, без исключений в вашей базе данных или ответах API.
- Используйте имена часовых поясов IANA — а не UTC-сдвиги — для предпочтений пользователей и повторяющихся расписаний.
- Никогда не парсите строки дат без явного часового пояса — поведение по умолчанию зависит от среды.
- Преобразовывайте в локальное время на уровне отображения — а не в бизнес-логике или запросах к базе данных.
- Для повторяющихся расписаний, храните правило — а не предварительно вычисленное время в UTC — чтобы переходы DST не повреждали будущие события.
- Тестируйте с крайними случаями — час перехода DST, даты вблизи международной линии даты и исторические метки времени в регионах, которые изменили сдвиг.
Установите наши расширения
Добавьте инструменты ввода-вывода в свой любимый браузер для мгновенного доступа и более быстрого поиска
恵 Табло результатов прибыло!
Табло результатов — это интересный способ следить за вашими играми, все данные хранятся в вашем браузере. Скоро появятся новые функции!
Подписаться на новости
все Новые поступления
всеОбновлять: Наш последний инструмент Добавлено 20 июня 2026 года
