В библиотеке js-joda работа с датой и временем построена вокруг
строгого разделения понятий: локальное время,
смещённое время (offset) и зональное время
(time zone). Это разделение является ключевым для понимания
различий между OffsetDateTime и
ZonedDateTime.
OffsetDateTime представляет собой дату и время,
привязанные к конкретному смещению от UTC, например
+03:00 или -05:00, но без учёта правил часовых
поясов и переходов на летнее/зимнее время.
Таким образом, OffsetDateTime — это момент времени,
выраженный через фиксированное смещение, а не через географическую
зону.
ZonedDateTime включает не только дату, время и смещение,
но и идентификатор временной зоны (например,
Europe/Moscow, America/New_York). Эта зона
определяет правила трансформации времени через исторические и текущие
правила ZoneRules.
OffsetDateTime оперирует фиксированным значением:
2025-01-10T10:00+03:00
ZonedDateTime оперирует зоной:
2025-01-10T10:00+03:00[Europe/Moscow]
В первом случае важен только offset. Во втором — зона, которая может изменять offset в зависимости от даты.
Смещение фиксировано и не изменяется:
OffsetDateTime.of(2025, 7, 1, 12, 0, 0, 0, ZoneOffset.of("+03:00"));
Даже если в реальности регион переходит на другое смещение, объект этого не отражает.
Использует правила зоны:
ZonedDateTime.of(2025, 7, 1, 12, 0, 0, 0, ZoneId.of("Europe/Berlin"));
Значение смещения будет вычислено автоматически и может отличаться от зимнего периода.
Оба типа могут представлять момент времени, но делают это по-разному:
OffsetDateTime — фиксированное представление
относительно UTCZonedDateTime — контекстное представление через правила
зоныОба типа могут быть преобразованы в Instant, но логика
различается только в источнике смещения.
const instant1 = offsetDateTime.toInstant();
const instant2 = zonedDateTime.toInstant();
Instant всегда нормализует время к UTC и является точкой
истины.
Основные операции:
withOffsetSameInstantwithOffsetSameLocaltoInstantplusDays, minusHourstoLocalDateTimegetOffsetПример:
import { OffsetDateTime, ZoneOffset } from '@js-joda/core';
const odt = OffsetDateTime.parse("2025-05-01T10:15:30+03:00");
const shifted = odt.withOffsetSameInstant(ZoneOffset.of("+05:00"));
Важно: смена offset может изменить локальное отображение времени, но не момент времени.
Основные операции:
withZoneSameInstantwithZoneSameLocalgetZonegetOffsettoInstantplusDays, withLaterOffsetAtOverlapПример:
import { ZonedDateTime, ZoneId } from '@js-joda/core';
const zdt = ZonedDateTime.parse("2025-05-01T10:15:30+03:00[Europe/Moscow]");
const shifted = zdt.withZoneSameInstant(ZoneId.of("America/New_York"));
Здесь пересчёт учитывает правила зоны и DST.
Сохраняет момент времени, меняет отображение:
UTC момент фиксирован → пересчёт offset
Аналогично, но через зону:
Instant фиксирован → пересчёт по ZoneRules
При преобразовании теряется информация о зоне:
const odt = zdt.toOffsetDateTime();
Остаётся только фиксированное смещение на конкретный момент.
Требуется указание зоны:
const zdt = odt.toZonedDateTime(ZoneId.of("Europe/Moscow"));
При этом применяется текущее правило зоны к уже зафиксированному моменту.
В ZonedDateTime возможны неоднозначности:
OffsetDateTime не имеет таких проблем, так как не
опирается на правила зоны.
Строковый формат:
2025-05-01T10:15:30+03:00
Простая структура без зоны.
Строковый формат:
2025-05-01T10:15:30+03:00[Europe/Moscow]
Содержит дополнительный контекст зоны, что важно для восстановления логики.
Ключевая особенность ZonedDateTime — использование
ZoneRules.
Они определяют:
OffsetDateTime полностью игнорирует эти правила.
Используется, когда:
Пример сценария:
Используется, когда:
Примеры:
Рассмотрим дату:
2025-06-01T12:00
OffsetDateTime.of(2025, 6, 1, 12, 0, 0, 0, ZoneOffset.of("+03:00"));
Всегда даёт фиксированный результат +03:00.
ZonedDateTime.of(2025, 6, 1, 12, 0, 0, 0, ZoneId.of("Europe/Moscow"));
Может вернуть другое смещение, если правила зоны изменятся в будущем или исторически отличаются.
Разделение типов можно выразить так:
OffsetDateTime — «время относительно UTC без
географии»ZonedDateTime — «время в конкретной географической зоне
с правилами»Эта граница определяет поведение всей системы работы со временем в js-joda и влияет на выбор типа при проектировании моделей данных.