Конвертация между календарями

Библиотека js-joda опирается на строгую модель времени, заимствованную из java.time. Центральной системой является ISO-8601 календарь, представленный типом LocalDate. Любые альтернативные календари рассматриваются как надстройки над базовой временной шкалой, а не как самостоятельные несовместимые системы.

Ключевой принцип: все календарные системы приводятся к единому числу дней с начала эпохи (epoch day), после чего выполняется обратное преобразование в нужную календарную систему.


Базовые типы для работы с календарями

LocalDate как фундамент

LocalDate — это ISO-датa без часового пояса:

import { LocalDate } from '@js-joda/core';

const isoDate = LocalDate.of(2026, 5, 25);

Вся конвертация между календарями начинается именно с этого типа.


ChronoLocalDate как обобщённый календарный интерфейс

Для поддержки альтернативных календарей используется ChronoLocalDate.

Он определяет единый набор операций:

  • получение эпохального дня (toEpochDay)
  • преобразование в ISO (toLocalDate)
  • доступ к компонентам даты (год, месяц, день)

Альтернативные календари в js-joda

Поддержка дополнительных календарных систем реализуется через пакет расширений:

import {
  HijrahDate,
  JapaneseDate,
  MinguoDate,
  ThaiBuddhistDate
} from '@js-joda/extra';

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

  • HijrahDate — исламский календарь
  • JapaneseDate — японская эра
  • MinguoDate — тайваньский календарь (Миньго)
  • ThaiBuddhistDate — буддийский календарь

Базовый принцип конвертации: epoch day

Любая дата в js-joda может быть сведена к числу дней от эпохи:

const iso = LocalDate.of(2026, 5, 25);
const epochDay = iso.toEpochDay();

Это ключевая точка перехода между календарями.


Конвертация ISO → альтернативный календарь

Hijrah (исламский календарь)

import { LocalDate } from '@js-joda/core';
import { HijrahDate } from '@js-joda/extra';

const iso = LocalDate.of(2026, 5, 25);

const hijrah = HijrahDate.from(iso);

Здесь выполняется:

  1. ISO-дата → epochDay
  2. epochDay → HijrahDate

JapaneseDate

import { JapaneseDate } from '@js-joda/extra';

const iso = LocalDate.of(2026, 5, 25);

const jp = JapaneseDate.from(iso);

Японский календарь учитывает эры (Reiwa, Heisei и т.д.), но хранит ту же абсолютную дату.


ThaiBuddhistDate

import { ThaiBuddhistDate } from '@js-joda/extra';

const iso = LocalDate.of(2026, 5, 25);

const thai = ThaiBuddhistDate.from(iso);

MinguoDate

import { MinguoDate } from '@js-joda/extra';

const iso = LocalDate.of(2026, 5, 25);

const minguo = MinguoDate.from(iso);

Обратная конвертация: альтернативный календарь → ISO

Любой ChronoLocalDate можно привести к LocalDate:

const isoBack = hijrah.toLocalDate();

или аналогично:

const isoFromJapanese = jp.toLocalDate();

Принцип одинаков для всех календарей:

  • извлечение epochDay
  • построение ISO-даты

Универсальная схема преобразований

ISO → ChronoLocalDate → ISO

const iso = LocalDate.of(2026, 5, 25);

const chrono = HijrahDate.from(iso);
const backToIso = chrono.toLocalDate();

Это демонстрирует, что альтернативные календари не разрывают модель данных, а лишь изменяют интерпретацию.


Работа через epochDay как низкоуровневый механизм

Прямое использование epochDay

const iso = LocalDate.of(2026, 5, 25);
const epoch = iso.toEpochDay();

Создание календаря из epochDay:

const hijrah = HijrahDate.ofEpochDay(epoch);
const japanese = JapaneseDate.ofEpochDay(epoch);

Эта форма является наиболее стабильной при любых преобразованиях.


Сравнение дат из разных календарей

Несмотря на различие систем, сравнение всегда происходит через абсолютную шкалу.

const a = HijrahDate.from(LocalDate.of(2026, 5, 25));
const b = JapaneseDate.from(LocalDate.of(2026, 5, 25));

const same = a.toEpochDay() === b.toEpochDay();

Результат всегда определяется не календарём, а моментом на временной оси.


Преобразование через ISO как промежуточный слой

Распространённый паттерн:

Любой календарь → ISO → другой календарь

const hijrah = HijrahDate.from(LocalDate.of(2026, 5, 25));

const iso = hijrah.toLocalDate();
const japanese = JapaneseDate.from(iso);

Такой подход гарантирует:

  • отсутствие потери точности
  • независимость от календарной реализации
  • единый промежуточный формат

Особенности японского календаря при конвертации

JapaneseDate содержит дополнительный уровень — эру:

import { JapaneseDate } from '@js-joda/extra';

const iso = LocalDate.of(2019, 5, 1);
const jp = JapaneseDate.from(iso);

При обратной конвертации:

const back = jp.toLocalDate();

Эра не влияет на epochDay, но влияет на представление года.


Ограничения и поведение при пограничных датах

Некоторые календарные системы имеют особенности:

Исламский календарь

  • лунный цикл
  • переменная длина года
  • возможны расхождения в локальных реализациях

Однако js-joda нормализует всё через epochDay, исключая неоднозначность.


Преобразование массивов дат между календарями

Типичный сценарий миграции данных:

const isoDates = [
  LocalDate.of(2026, 1, 1),
  LocalDate.of(2026, 2, 1),
  LocalDate.of(2026, 3, 1)
];

const hijrahDates = isoDates.map(HijrahDate.from);

Обратное преобразование:

const backToIso = hijrahDates.map(d => d.toLocalDate());

Сериализация и восстановление календарных объектов

При хранении данных рекомендуется использовать ISO как универсальный формат:

const iso = LocalDate.of(2026, 5, 25);

const serialized = iso.toString(); // "2026-05-25"

Восстановление:

const restored = LocalDate.parse(serialized);

А затем конвертация:

const hijrah = HijrahDate.from(restored);

Согласованность временной модели

Все календарные системы в js-joda подчиняются следующим правилам:

  • единая абсолютная шкала времени (epochDay)
  • отсутствие скрытых временных зон
  • неизменяемость объектов даты
  • обратимость преобразований через ISO

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


Итоговая схема взаимодействия систем

ChronoLocalDate (любая система)
        ↓
     epochDay
        ↓
   LocalDate (ISO)
        ↓
ChronoLocalDate (другая система)

Такая архитектура делает конвертацию между календарями симметричной и воспроизводимой во всех поддерживаемых типах js-joda.