0Pricing
Coding Interview Prep · Урок

Часовые пояса и временные метки

Хранение времени в UTC, преобразование часовых поясов и вопросы о временных метках, которые задают на собеседованиях

«Часовые пояса и временные метки» — бесплатный урок Coding Interview Prep на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Coding Interview Prep, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Coding Interview Prep содержит 4 уроков всего.

Почему часовые пояса ставят кандидатов в тупик

На часовых поясах даже уверенные кандидаты часто теряются, поэтому интервьюеры проверяют эту тему, чтобы понять глубину знаний. Главный вопрос всегда один: «как хранить и сравнивать временные метки в разных регионах?»

Профессиональный ответ — это дисциплина, а не функция: храните всё в UTC, а преобразование выполняйте только на границах системы при отображении. Правильно выберите модель хранения — и большинство запросов станет простым.

  • временная метка и временная метка с часовым поясом
  • Преобразование между часовыми поясами
  • UTC как эталонный источник данных

Временная метка и временная метка с часовым поясом

В PostgreSQL есть два типа временных меток, и их смешение — одна из самых распространенных ошибок на собеседованиях.

  • timestamp (без часового пояса): значение показаний часов, к которому не привязан часовой пояс. Сохраняется ровно то, что было передано.
  • timestamptz (с часовым поясом): внутри хранится в UTC; при вводе значение преобразуется из часового пояса сеанса, а при выводе преобразуется обратно.

Несмотря на название, timestamptz не хранит часовой пояс; он хранит точный момент времени в UTC. Эта деталь производит хорошее впечатление на интервьюеров.

CREATE TABLE events (
  id          bigint,
  occurred_at timestamptz   -- recommended: an absolute instant
);

Храните UTC, преобразуйте на границах системы

Главное правило. Сохраняйте моменты времени в UTC (используйте timestamptz), а в местный часовой пояс преобразуйте только при отображении данных пользователю. Это устраняет неоднозначность, связанную с переходами на летнее и зимнее время, и позволяет везде правильно упорядочивать данные по времени.

Если Вас спросят «почему UTC?», ответьте: в UTC нет переходов на летнее и зимнее время, поэтому одно и то же показание местных часов никогда не повторяется и не пропускается, в отличие от местного времени.

-- Display a UTC instant in a user's zone (Postgres)
SELECT occurred_at AT TIME ZONE 'America/New_York' AS local_time
FROM events;

Двойное значение AT TIME ZONE

AT TIME ZONE — хитрый оператор и частый источник ошибок, потому что в зависимости от типа входных данных он выполняет два противоположных действия:

  • Для timestamptz он преобразует абсолютный момент в эту часовую зону и возвращает обычный timestamp (показания часов в этой зоне).
  • Для обычного timestamp он интерпретирует эти показания часов как время в этой зоне и возвращает timestamptz.

Весь секрет — понимать, в каком направлении выполняется преобразование.

-- timestamptz -> local wall clock (returns timestamp)
SELECT TIMESTAMPTZ '2024-03-01 12:00:00+00'
         AT TIME ZONE 'Asia/Tokyo';        -- 2024-03-01 21:00:00

-- plain timestamp interpreted in a zone (returns timestamptz)
SELECT TIMESTAMP '2024-03-01 12:00:00'
         AT TIME ZONE 'Asia/Tokyo';        -- 2024-03-01 03:00:00+00

Получение текущего момента

Важно понимать, что возвращают функции для получения текущего времени. NOW() и CURRENT_TIMESTAMP возвращают в Postgres значение типа timestamptz. Они возвращают время начала транзакции, а не оператора, что важно при длительных транзакциях.

Чтобы явно получить UTC, выполните преобразование: NOW() AT TIME ZONE 'UTC'. В MySQL функция UTC_TIMESTAMP() сразу возвращает время UTC.

SELECT
  NOW()                       AS tx_start_tz,
  NOW() AT TIME ZONE 'UTC'    AS utc_walltime;

Переходы на летнее время — настоящий враг

Интервьюеры любят пограничные случаи, связанные с DST. При переходе на летнее время локальный час не существует; при возврате на стандартное время час повторяется. Хранение местного времени делает такие значения неоднозначными или недопустимыми.

Хранение UTC полностью устраняет эту проблему: каждый момент уникален и упорядочен монотонно. Указание региона, например 'America/New_York', а не фиксированного смещения вроде -05:00, позволяет базе данных правильно применять правила DST для любой даты.

-- Region name applies DST automatically for the given date
SELECT TIMESTAMPTZ '2024-07-01 12:00:00+00'
         AT TIME ZONE 'America/New_York' AS summer, -- EDT (-04)
       TIMESTAMPTZ '2024-01-01 12:00:00+00'
         AT TIME ZONE 'America/New_York' AS winter; -- EST (-05)

Группировка по местному дню в разных часовых зонах

Реалистичная задача: «ежедневно активные пользователи в местном времени каждого пользователя». Если напрямую усечь метку времени UTC, границы полуночи будут неверными для пользователей не из зоны UTC.

Преобразуйте время в часовую зону пользователя до усечения до дня. Преобразование смещает показания часов так, чтобы границы дня совпадали с местными.

SELECT
  DATE_TRUNC('day', occurred_at AT TIME ZONE u.tz) AS local_day,
  COUNT(DISTINCT e.user_id)                         AS dau
FROM events e
JOIN users u ON u.id = e.user_id
GROUP BY 1
ORDER BY 1;

Безопасное сравнение меток времени

При фильтрации по столбцу timestamptz сравнивайте его с явным моментом времени — в идеале с литералом UTC или с timestamptz со смещением. Сравнение с обычной строкой может привести к её интерпретации в непредсказуемой часовой зоне сеанса.

Так сравнение остаётся однозначным независимо от того, кто выполняет запрос.

SELECT *
FROM events
WHERE occurred_at >= TIMESTAMPTZ '2024-03-01 00:00:00+00'
  AND occurred_at <  TIMESTAMPTZ '2024-04-01 00:00:00+00';

Эпоха и метки времени Unix

Многие системы хранят время как эпоху Unix — число секунд с 1970-01-01 UTC. На собеседовании Вам могут дать числовой столбец и попросить преобразовать его в дату и время.

  • Postgres: TO_TIMESTAMP(epoch_seconds) возвращает значение типа timestamptz.
  • Обратное преобразование в эпоху: EXTRACT(EPOCH FROM occurred_at).
  • MySQL: FROM_UNIXTIME() и UNIX_TIMESTAMP().

Значения эпохи по своей сути относятся к UTC, что отчасти объясняет их популярность для хранения.

SELECT
  TO_TIMESTAMP(1709294400)               AS as_ts,   -- from epoch
  EXTRACT(EPOCH FROM NOW())::bigint        AS as_epoch; -- to epoch

Заметки о часовых зонах в разных диалектах

Краткая карта, которая поможет Вам уверенно работать в любой среде:

  • Postgres: timestamptz и AT TIME ZONE обеспечивают наиболее широкие возможности.
  • MySQL: TIMESTAMP автоматически преобразуется через сеанс time_zone; CONVERT_TZ(t, from, to) выполняет явное преобразование. У DATETIME нет сведений о часовой зоне.
  • SQL Server: datetimeoffset хранит смещение; AT TIME ZONE 'name' выполняет преобразование с использованием названий зон Windows.
-- MySQL explicit conversion
SELECT CONVERT_TZ(event_dt, 'UTC', 'Europe/Istanbul') AS local_dt
FROM events;

Более глубокий пример: сеансы, переходящие через полночь

Тонкий вопрос по отчётности: как посчитать сеансы по местному календарному дню, если сеанс может перейти через полночь? Решение основано на том же принципе: преобразуйте время в местное, а затем распределите его по группам.

Храните начало и конец как timestamptz; для отчётности определяйте местный день по преобразованному времени начала. Если сеанс нужно разделить между двумя днями, соедините данные с таблицей последовательных дней — это отличный момент, который стоит упомянуть как дополнительный вопрос.

SELECT
  DATE_TRUNC('day', started_at AT TIME ZONE 'Europe/Istanbul') AS local_day,
  COUNT(*) AS sessions
FROM sessions
GROUP BY 1
ORDER BY 1;

Проверка

Подтвердите рекомендуемую стратегию хранения и объясните почему.

Итоги: часовые зоны и метки времени

Главный принцип, который следует запомнить:

  • Храните UTC как timestamptz; преобразуйте его в именованную часовую зону только для отображения.
  • timestamptz хранит момент времени в UTC, а не часовую зону, несмотря на название.
  • AT TIME ZONE работает в обоих направлениях в зависимости от типа входных данных: преобразует timestamptz в местное показание часов или интерпретирует обычный timestamp как время в указанной зоне.
  • Используйте названия регионов, например 'America/New_York', чтобы DST применялся автоматически; избегайте фиксированных смещений.
  • Преобразуйте время в местное до усечения до дня и сравнивайте столбцы с явно заданными моментами времени UTC.

Часто задаваемые вопросы

Урок «Часовые пояса и временные метки» бесплатный?

Да — полный текст урока «Часовые пояса и временные метки» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Coding Interview Prep, подпишись на CoddyKit PRO. Курс Coding Interview Prep содержит 4 уроков всего.

Чему я научусь в уроке «Часовые пояса и временные метки»?

Хранение времени в UTC, преобразование часовых поясов и вопросы о временных метках, которые задают на собеседованиях Ты практикуешь Coding Interview Prep с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Coding Interview Prep?

Предыдущий опыт не требуется. Coding Interview Prep на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Часовые пояса и временные метки»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Coding Interview Prep?

Да. Каждый урок Coding Interview Prep включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Арифметика дат и интервалы
  2. Округление дат и распределение по интервалам
  3. Разбор и форматирование строк
  4. Часовые пояса и временные метки
← Назад к Coding Interview Prep