Работа с високосными годами

В григорианском календаре високосный год вводится для компенсации расхождения между астрономическим и календарным годом. Базовое правило формализуется через делимость:

y = 4k ;; (y 100m ;; y = 400n)

Иными словами, год считается високосным, если он кратен 4, но исключаются годы, кратные 100, за исключением тех, что кратны 400. Это правило является фундаментом работы большинства календарных систем, включая те, которые используются в библиотеке Globalize.

Представление календарей в Globalize

Библиотека Globalize опирается на данные CLDR (Unicode Common Locale Data Repository), где календарные правила уже формализованы и структурированы. Для григорианского календаря, который используется по умолчанию в большинстве локалей, информация о високосных годах не вычисляется вручную в библиотеке, а следует стандарту CLDR.

Это означает:

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

Форматирование дат, затрагивающих 29 февраля

Globalize использует встроенные механизмы форматирования дат, основанные на ICU/CLDR шаблонах. При работе с датами, содержащими 29 февраля, библиотека не требует дополнительных проверок — корректность определяется самим календарём.

import Globalize from "globalize";

// предположим, что CLDR данные уже загружены
const globalize = new Globalize("ru");

const leapDate = new Date(2024, 1, 29); // 29 февраля 2024

console.log(
  globalize.formatDate(leapDate, {
    datetime: "medium"
  })
);

Поведение при форматировании определяется локалью, но логика високосного года остаётся неизменной для всех языков.

Разница между вычислением и отображением дат

Важно разделять два уровня работы с календарём:

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

Globalize работает исключительно на уровне представления. Это означает:

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

Проверка корректности дат при локализации

При работе с пользовательским вводом возможны ситуации, когда дата 29 февраля вводится для года, не являющегося високосным. В таких случаях поведение зависит от реализации JavaScript Date, а Globalize лишь интерпретирует уже нормализованное значение.

Пример обработки:

const date = new Date(2023, 1, 29);

console.log(date.toString());

JavaScript автоматически преобразует такую дату в 1 марта 2023 года, и Globalize будет форматировать уже скорректированное значение.

Локализация названий месяцев и влияние високосных лет

В CLDR данные о месяцах не зависят от високосности года. Однако разница проявляется в длине февраля:

  • обычный год — 28 дней;
  • високосный год — 29 дней.

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

Работа с относительными датами

При использовании относительного времени важно учитывать корректность базовой даты. Например, вычисление разницы между 28 февраля и 1 марта в високосный год отличается от невисокосного сценария.

import Globalize from "globalize";

const globalize = new Globalize("ru");

const from = new Date(2024, 1, 28);
const to = new Date(2024, 2, 1);

console.log(
  globalize.formatRelativeTime(
    (to - from) / (1000 * 60 * 60 * 24),
    "day"
  )
);

Здесь результат зависит от количества фактических дней между датами, а не от логики календарных переходов.

CLDR как источник календарной истины

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

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

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

Особенности сериализации и парсинга дат

При разборе строковых представлений дат Globalize опирается на формат локали. Например:

const parsed = globalize.parseDate("29.02.2024", {
  skeleton: "yMd"
});

console.log(parsed);

Корректный разбор возможен только при наличии согласованного календарного контекста. Если дата некорректна (например, 29.02.2023), результат будет зависеть от внутренней нормализации JavaScript Date.

Влияние временных зон на високосные даты

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

  • локальное время может сдвигать дату относительно UTC;
  • форматирование Globalize использует системный Date, уже учитывающий временную зону;
  • 29 февраля остаётся корректной календарной точкой независимо от зоны.

Поведение при граничных переходах

Особое внимание требуется при операциях инкремента дат:

  • добавление 1 дня к 28 февраля в високосный год приводит к 29 февраля;
  • добавление 1 месяца может вести к нормализации даты в зависимости от реализации Date.

Globalize не изменяет это поведение, но корректно отображает итоговое значение:

const d = new Date(2024, 1, 28);
d.setDate(d.getDate() + 1);

console.log(globalize.formatDate(d, { datetime: "short" }));

Совместимость с ICU форматами

Форматы ICU, используемые Globalize, не содержат отдельной логики для високосных годов. Вместо этого:

  • календарные шаблоны универсальны;
  • различия обрабатываются на уровне данных CLDR;
  • форматирование остаётся детерминированным.

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