Календарные системы

В экосистеме FormatJS работа с датами строится на базе стандарта ECMA-402 (Intl API) и данных CLDR (Common Locale Data Repository). Календарная система в этом контексте определяет не только формат отображения даты, но и саму модель исчисления времени: начало эпохи, структуру года, месяцы, правила нумерации и особенности локализации.

Роль календарной системы в форматировании дат

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

  • различный старт эпохи (например, 1970 vs 0001);
  • альтернативные названия месяцев;
  • иной порядок полей даты;
  • отличающиеся правила високосных лет;
  • культурно обусловленные форматы записи.

В JavaScript календарь задаётся через параметр calendar в Intl.DateTimeFormat.

new Intl.DateTimeFormat('en-US', {
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  calendar: 'gregory'
}).format(new Date());

Поддержка календарей в CLDR и Intl

Большинство библиотек интернационализации, включая FormatJS, опираются на данные CLDR. Именно CLDR определяет список доступных календарных систем:

  • gregory (Григорианский)
  • buddhist (Буддийский)
  • chinese (Китайский лунно-солнечный)
  • coptic (Коптский)
  • ethiopic (Эфиопский)
  • hebrew (Еврейский)
  • indian (Индийский национальный)
  • islamic (Исламский)
  • iso8601
  • japanese (Японский императорский)

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

Григорианский календарь как базовая система

Григорианский календарь является дефолтным для большинства локалей и платформ.

Особенности:

  • используется эпоха «до/после Рождества Христова»;
  • 12 месяцев фиксированной длины (с вариацией дней);
  • стандартная модель високосного года;
  • совместимость с ISO 8601.
new Intl.DateTimeFormat('ru-RU', {
  calendar: 'gregory',
  year: 'numeric',
  month: '2-digit',
  day: '2-digit'
}).format(new Date());

Буддийский календарь

Буддийская система широко используется в Таиланде. Основное отличие — смещение эпохи.

  • год = григорианский год + 543;
  • структура месяцев идентична григорианской;
  • используется в государственных документах в ряде стран.
new Intl.DateTimeFormat('th-TH', {
  calendar: 'buddhist',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
}).format(new Date());

Японский календарь (эры императоров)

Японская система основана на эрах правления императоров.

Особенности:

  • год начинается заново при смене императора;
  • используется обозначение эры (Reiwa, Heisei и др.);
  • требует сопоставления даты с исторической эпохой.
new Intl.DateTimeFormat('ja-JP', {
  calendar: 'japanese',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
}).format(new Date());

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

Исламский календарь является лунным и отличается от солнечных систем.

Ключевые характеристики:

  • 12 лунных месяцев;
  • год короче григорианского примерно на 10–11 дней;
  • смещение дат по сезонам;
  • важен для религиозных событий.
new Intl.DateTimeFormat('ar-SA', {
  calendar: 'islamic',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
}).format(new Date());

Еврейский календарь

Еврейская система объединяет лунные и солнечные циклы.

Особенности:

  • сложная система високосных месяцев;
  • годы имеют переменную длину;
  • используется для религиозных праздников;
  • отсчёт от условной даты сотворения мира.
new Intl.DateTimeFormat('he-IL', {
  calendar: 'hebrew',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
}).format(new Date());

Китайский календарь

Китайская система является лунно-солнечной и включает циклическую модель.

Особенности:

  • 12-летний цикл животных;
  • вставочные месяцы для синхронизации с солнцем;
  • важность для традиционных праздников;
  • вариативность длины года.
new Intl.DateTimeFormat('zh-CN', {
  calendar: 'chinese',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
}).format(new Date());

Эфиопский и коптский календари

Эти системы сохраняют древние традиции исчисления времени.

Общие черты:

  • альтернативная эпоха;
  • 13 месяцев (12 по 30 дней + короткий месяц);
  • сдвиг относительно григорианского календаря.
new Intl.DateTimeFormat('am-ET', {
  calendar: 'ethiopic',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
}).format(new Date());

ISO 8601 календарь

ISO 8601 представляет собой технический стандарт, используемый в вычислительных системах.

Особенности:

  • неделя начинается с понедельника;
  • год может быть разделён на недельные периоды;
  • используется в бизнес-логике и API.
new Intl.DateTimeFormat('en-GB', {
  calendar: 'iso8601',
  year: 'numeric',
  month: '2-digit',
  day: '2-digit'
}).format(new Date());

Интеграция календарей в FormatJS

Библиотека FormatJS использует Intl API как базовый слой, поэтому календарные системы доступны через стандартные параметры форматирования.

В react-intl календарь применяется через низкоуровневые API или кастомные форматтеры:

import { createIntl, createIntlCache } from 'react-intl';

const cache = createIntlCache();

const intl = createIntl({
  locale: 'en-US'
}, cache);

intl.formatDate(new Date(), {
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  calendar: 'buddhist'
});

Поведение локали и календаря

Локаль и календарь взаимодействуют, но не являются взаимозависимыми:

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

Пример:

new Intl.DateTimeFormat('en-US', {
  calendar: 'japanese',
  year: 'numeric',
  month: 'long'
}).format(new Date());

Здесь английская локаль не меняет японскую систему летоисчисления, но влияет на язык вывода.

Ограничения и особенности реализации

  • не все браузеры одинаково поддерживают редкие календари;
  • часть календарей доступна только при полной ICU-данных сборке;
  • некоторые комбинации локалей и календарей дают fallback к gregory;
  • формат отображения может различаться между платформами.

Особенно важно учитывать:

  • серверный рендеринг (Node.js требует полной ICU);
  • кэширование форматтеров;
  • различие между display-именами и внутренними значениями.

Поведение в React-интерфейсах

При использовании react-intl календарь влияет на все компоненты форматирования:

  • <FormattedDate />
  • intl.formatDate
  • useIntl()

Пример:

<FormattedDate
  value={new Date()}
  year="numeric"
  month="long"
  day="2-digit"
  calendar="hebrew"
/>

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

Хотя стандарт Intl ограничен набором календарей, архитектура FormatJS позволяет:

  • создавать кастомные форматтеры;
  • трансформировать даты перед форматированием;
  • комбинировать несколько систем отображения.

Пример адаптации:

const buddhistYearOffset = 543;

function formatBuddhistYear(date) {
  const year = date.getFullYear() + buddhistYearOffset;
  return year;
}

Такой подход применяется при необходимости полной независимости от Intl.

Производительность форматирования

Календарные системы влияют на:

  • стоимость создания Intl.DateTimeFormat;
  • кэширование форматтеров;
  • частоту пересоздания объектов;
  • SSR-рендеринг.

Рекомендуется:

  • переиспользовать форматтеры;
  • хранить экземпляры Intl.DateTimeFormat;
  • избегать создания внутри render-циклов.
const formatter = new Intl.DateTimeFormat('en-US', {
  calendar: 'islamic',
  year: 'numeric'
});

Влияние CLDR-данных

CLDR определяет:

  • локализованные названия месяцев для каждого календаря;
  • правила отображения эр;
  • особенности сокращений;
  • числовые системы.

Без CLDR невозможно корректно отобразить календарь в многоязычных интерфейсах, что делает его фундаментальной частью интернационализации в FormatJS.