В основе работы с временными зонами в js-joda лежит строгая модель разделения понятий:
Europe/Moscow)Ключевой принцип: любое преобразование между зонами происходит не
через «изменение времени», а через пересчёт одного и того же момента
времени (Instant) в другой локальный контекст.
Абсолютное время в js-joda представлено объектом
Instant. Он не зависит от временных зон и всегда
интерпретируется в UTC.
import { Instant } from '@js-joda/core';
const instant = Instant.now();
При работе с зонами Instant используется как
промежуточное представление:
InstantInstant → локальное время в другой зонеЭто устраняет ошибки, связанные с ручным пересчётом смещений.
ZonedDateTime объединяет:
import { ZonedDateTime, ZoneId } from '@js-joda/core';
const zone = ZoneId.of('Europe/Moscow');
const zdt = ZonedDateTime.now(zone);
Каждый объект ZonedDateTime всегда однозначно связан с
конкретным моментом времени.
Часто требуется задать локальное время в определённой зоне:
import { ZonedDateTime, ZoneId, LocalDateTime } from '@js-joda/core';
const zone = ZoneId.of('Asia/Almaty');
const ldt = LocalDateTime.of(2026, 5, 24, 12, 0);
const zdt = ZonedDateTime.of(ldt, zone);
Важно: такой способ фиксирует локальное время, а затем интерпретирует его в рамках правил зоны.
Наиболее корректный способ конвертации — через абсолютный момент времени.
ZonedDateTimeInstantInstant интерпретируется в новой зонеimport { ZoneId, ZonedDateTime } from '@js-joda/core';
const zoneTokyo = ZoneId.of('Asia/Tokyo');
const zoneLondon = ZoneId.of('Europe/London');
const tokyoTime = ZonedDateTime.now(zoneTokyo);
const londonTime = tokyoTime.withZoneSameInstant(zoneLondon);
withZoneSameInstantМетод сохраняет момент времени, изменяя только представление:
Сохраняет момент времени:
const converted = zdt.withZoneSameInstant(ZoneId.of('UTC'));
Используется при:
Сохраняет локальные поля, меняя момент времени:
const shifted = zdt.withZoneSameLocal(ZoneId.of('Europe/Berlin'));
Используется реже, так как:
Иногда удобнее выполнять преобразование вручную:
import { ZonedDateTime, ZoneId } from '@js-joda/core';
const source = ZonedDateTime.now(ZoneId.of('Asia/Seoul'));
const instant = source.toInstant();
const target = instant.atZone(ZoneId.of('America/New_York'));
Этот способ подчёркивает фундаментальную модель:
UTC часто используется как универсальный слой хранения:
const utcTime = zdt.withZoneSameInstant(ZoneId.UTC);
или:
const utcInstant = zdt.toInstant();
Преимущества:
js-joda учитывает правила переходов автоматически через
ZoneRules.
При конвертации:
В момент перехода «назад» время может повторяться:
01:30 может существовать дважды
js-joda разрешает такие ситуации через правила зоны:
При создании времени из LocalDateTime возможны конфликты:
const zdt = ZonedDateTime.of(LocalDateTime.of(2026, 10, 25, 2, 30), zone);
Если такое время неоднозначно:
Сравнение всегда производится через Instant:
const a = ZonedDateTime.now(ZoneId.of('Asia/Tokyo'));
const b = ZonedDateTime.now(ZoneId.of('Europe/Moscow'));
const result = a.toInstant().isBefore(b.toInstant());
Прямое сравнение локальных значений недопустимо, так как:
Типичный сценарий:
const instant = zdt.toInstant().toString();
import { Instant, ZoneId } from '@js-joda/core';
const restored = Instant.parse(instant).atZone(ZoneId.of('Europe/Paris'));
Такой подход гарантирует:
При построении расписаний часто требуется отображение одного события в разных зонах:
const base = ZonedDateTime.of(
LocalDateTime.of(2026, 6, 1, 10, 0),
ZoneId.of('UTC')
);
const zones = [
ZoneId.of('Europe/London'),
ZoneId.of('Asia/Dubai'),
ZoneId.of('America/New_York')
];
const mapped = zones.map(z => base.withZoneSameInstant(z));
Каждый элемент массива отражает один и тот же момент времени.
// неправильный подход
const wrong = date + 3 * 60 * 60 * 1000;
Проблема:
LocalDateTime не содержит зоны:
zdt.withZoneSameInstant(zone1).withZoneSameInstant(zone2);
Хотя технически допустимо, часто приводит к:
В распределённых системах используется единый принцип:
InstantInstantZoneIdconst instant = ZonedDateTime.now(ZoneId.of('Asia/Almaty')).toInstant();
Вся работа с зонами сводится к цепочке:
LocalDateTime + ZoneId → ZonedDateTime → Instant → ZonedDateTime (новая зона)
Эта модель обеспечивает: