Работа с датой и временем в современных приложениях требует строгого разделения между календарным временем и его представлением в различных часовых зонах. В библиотеке js-joda эта модель реализована через два ключевых концепта: фиксированное смещение от UTC и идентифицируемую часовую зону с правилами перехода. Эти сущности представлены типами ZoneOffset и ZoneId, которые лежат в основе всей зональной арифметики времени.
ZoneOffset представляет фиксированное смещение относительно UTC. Это значение выражается в часах, минутах и секундах и не зависит от географической области или сезонных изменений.
Фактически ZoneOffset описывает простое правило преобразования:
локальное время = UTC + смещение
Типичные примеры смещений:
В js-joda смещение создаётся через статические методы:
import { ZoneOffset } from '@js-joda/core';
const offset1 = ZoneOffset.of('+03:00');
const offset2 = ZoneOffset.ofHours(3);
const offset3 = ZoneOffset.ofHoursMinutes(5, 30);
Каждый вариант фиксирует конкретное смещение, которое не изменяется во времени.
ZoneOffset не содержит информации о правилах перехода времени. Это означает:
Такой подход делает ZoneOffset полезным в системах, где требуется стабильное смещение без временных корректировок:
Однако для реальных часовых поясов, где присутствуют сезонные изменения, этого недостаточно.
ZoneId представляет идентификатор часовой зоны, основанный на международной базе IANA Time Zone Database. В отличие от ZoneOffset, ZoneId описывает не только смещение, но и набор правил, определяющих изменение этого смещения во времени.
Примеры ZoneId:
Europe/MoscowAsia/AlmatyAmerica/New_YorkКаждая такая зона содержит:
ZoneId в js-joda выступает как абстракция над:
В отличие от ZoneOffset, ZoneId не является фиксированным значением.
Создание часовой зоны происходит через метод
ZoneId.of:
import { ZoneId } from '@js-joda/core';
const zone1 = ZoneId.of('Europe/Moscow');
const zone2 = ZoneId.of('Asia/Almaty');
const zone3 = ZoneId.of('America/New_York');
Также существует системная зона:
const systemZone = ZoneId.systemDefault();
Системная зона определяется окружением выполнения (операционная система или конфигурация среды).
Ключевое различие заключается в природе смещения.
| Характеристика | ZoneOffset | ZoneId |
|---|---|---|
| Тип | фиксированное смещение | идентификатор часовой зоны |
| Правила DST | отсутствуют | присутствуют |
| Исторические изменения | отсутствуют | учитываются |
| Привязка к региону | отсутствует | есть |
| Пример | +03:00 | Europe/Moscow |
ZoneOffset представляет одно значение смещения, тогда как ZoneId описывает поведение времени в течение всего календарного года и исторического периода.
ZoneId опирается на ZoneRules, которые определяют:
При вычислении времени библиотека использует алгоритм:
Таким образом ZoneId выступает как контейнер логики, а ZoneOffset — как результат этой логики для конкретного момента времени.
ZoneId становится практически значимым в связке с типом ZonedDateTime, где дата и время интерпретируются в конкретной зоне.
import { ZonedDateTime, ZoneId, LocalDateTime } from '@js-joda/core';
const zone = ZoneId.of('Asia/Almaty');
const local = LocalDateTime.of(2026, 1, 10, 12, 0);
const zoned = local.atZone(zone);
В этом случае ZoneId определяет, какое смещение будет применено к локальному времени.
ZoneOffset также может использоваться для создания ZonedDateTime, но поведение будет отличаться:
import { ZonedDateTime, ZoneOffset, LocalDateTime } from '@js-joda/core';
const offset = ZoneOffset.of('+03:00');
const local = LocalDateTime.of(2026, 1, 10, 12, 0);
const zoned = local.atZone(offset);
Здесь отсутствуют правила перехода, и смещение остаётся фиксированным.
Использование ZoneOffset вместо ZoneId приводит к потере информации о реальном поведении времени.
Например:
Это критично при работе с историческими данными и планированием событий.
ZoneId основан на стандарте IANA Time Zone Database. Каждый идентификатор имеет формат:
Region/City
Примеры:
Такой формат обеспечивает:
ZoneId не является статичным значением в смысле смещения. Его поведение может изменяться при обновлении базы часовых зон.
Например:
Таким образом, ZoneId представляет не число, а набор правил, зависящий от версии базы данных.
ZoneId может возвращать актуальный ZoneOffset для конкретного момента времени:
import { ZoneId, Instant } from '@js-joda/core';
const zone = ZoneId.of('Asia/Almaty');
const instant = Instant.now();
const offset = zone.rules().offset(instant);
В этом процессе ZoneId выступает как источник правил, а ZoneOffset — как вычисленный результат.
ZoneId позволяет корректно моделировать:
ZoneOffset подходит только для фиксированных преобразований, тогда как ZoneId обеспечивает корректность в долгосрочной перспективе.
ZoneOffset описывает «на сколько часов смещено время», тогда как ZoneId описывает «как время ведёт себя в конкретном регионе».
Эта разница определяет архитектурный подход: