Кеширование зон

В библиотеке js-joda работа с часовыми поясами построена вокруг неизменяемых объектов ZoneId и набора правил ZoneRules. Архитектура сознательно повторяет подход Java Time API, где ключевой оптимизацией выступает переиспользование уже созданных экземпляров зон и их правил вместо повторного построения.

Модель работы ZoneId и ZoneRules

ZoneId представляет собой идентификатор часового пояса (например, Europe/Moscow или Asia/Almaty). Сам по себе объект не хранит сложную логику, а выступает ссылкой на набор правил.

ZoneId ZoneRules

ZoneRules содержит детальное описание поведения зоны:

  • смещение относительно UTC;
  • правила перехода на летнее/зимнее время;
  • исторические изменения (transition rules);
  • таблицы смещений по датам.

Именно ZoneRules является наиболее «тяжёлой» частью модели, поскольку включает в себя набор интервалов и переходов, которые могут покрывать десятки лет истории.

Внутреннее кеширование ZoneId

В js-joda идентификаторы зон создаются через фабричный метод:

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

const zone = ZoneId.of('Europe/Moscow');

Повторные вызовы ZoneId.of('Europe/Moscow') не обязаны создавать новый объект. Внутри используется кеширование идентификаторов, чтобы:

  • избежать повторного парсинга строки;
  • снизить количество аллокаций;
  • обеспечить идентичность объектов для одного и того же ID.

Поведение можно описать как ленивое хранение:

первый вызов → парсинг строки → создание ZoneId → запись в кеш
последующие вызовы → возврат из кеша

Это критично для сценариев, где работа с датами происходит в циклах или при обработке потоков событий.

Кеширование ZoneRules и переходов времени

Основная нагрузка приходится на ZoneRules. При первом обращении к зоне библиотека загружает и вычисляет набор правил, после чего они сохраняются.

Кешируются:

  • базовое смещение;
  • таблицы DST-переходов;
  • исторические корректировки;
  • вычисленные интервалы смещений.

При повторном использовании той же зоны:

const z = ZoneId.of('Asia/Almaty');
const rules = z.rules();

rules() возвращает уже готовый объект без повторной загрузки данных.

Важная особенность: кешируется не только сам объект правил, но и результаты вычислений для конкретных временных точек.

Роль js-joda-timezone и статических данных

В чистом @js-joda/core отсутствуют данные о реальных часовых поясах. Для работы с ними используется пакет расширения:

  • @js-joda/timezone

Он содержит предсобранные TZDB-данные (IANA Time Zone Database), которые:

  • загружаются один раз при инициализации;
  • преобразуются в структуру ZoneRulesProvider;
  • кэшируются в памяти процесса.

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

Кеширование при создании ZonedDateTime

При создании объектов ZonedDateTime кеширование проявляется косвенно через повторное использование зон:

import { ZonedDateTime, ZoneId, LocalDateTime } from '@js-joda/core';

const zone = ZoneId.of('Europe/Moscow');

const zdt1 = ZonedDateTime.of(LocalDateTime.now(), zone);
const zdt2 = ZonedDateTime.of(LocalDateTime.now(), zone);

Обе операции используют один и тот же экземпляр ZoneId, а значит:

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

Основная стоимость остаётся в вычислении LocalDateTime, а не в работе с зоной.

Повторное использование ZoneId как стратегия оптимизации

В высоконагруженных приложениях повторный парсинг строковых идентификаторов становится узким местом. Поэтому применяется явное кеширование на уровне приложения:

const ZONES = Object.freeze({
  MOSCOW: ZoneId.of('Europe/Moscow'),
  ALMATY: ZoneId.of('Asia/Almaty'),
  LONDON: ZoneId.of('Europe/London')
});

Такой подход даёт несколько эффектов:

  • исключение повторного вызова ZoneId.of;
  • уменьшение нагрузки на внутренний кеш библиотеки;
  • стабильные ссылки на объекты зон;
  • предсказуемость поведения при сравнении ссылок.

Локальное кеширование и memoization

В динамических сценариях, где зоны приходят извне, применяется мемоизация:

const zoneCache = new Map();

function getZone(id) {
  if (!zoneCache.has(id)) {
    zoneCache.set(id, ZoneId.of(id));
  }
  return zoneCache.get(id);
}

Это особенно важно при обработке:

  • пользовательских настроек часовых поясов;
  • данных из API;
  • логов с различными регионами.

Кеширование и переходы DST

Наиболее сложная часть — обработка переходов летнего времени. Для каждой зоны существует набор правил, который может изменяться во времени.

ZoneRules кеширует уже рассчитанные переходы, чтобы избежать повторных вычислений:

  • переходы «лето → зима»;
  • переходы «зима → лето»;
  • исключения в отдельных годах.

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

const zone = ZoneId.of('Europe/Berlin');
const rules = zone.rules();

rules.offset(Instant.now());

Каждый вызов использует уже подготовленные интервалы.

Память и стоимость кеширования

Кеширование зон и правил увеличивает потребление памяти, но снижает вычислительную стоимость операций с датами. В типичных приложениях баланс смещён в сторону кеширования, поскольку:

  • набор зон ограничен (IANA database фиксирована);
  • повторное использование зон встречается постоянно;
  • вычисления правил дороже хранения ссылок.

Таким образом, модель js-joda ориентирована на долгоживущие кеши и минимизацию повторной работы с временными зонами.