Форматирование с учётом локали

В библиотеке js-joda форматирование даты и времени строится вокруг неизменяемых объектов времени и отдельного слоя форматирования — DateTimeFormatter. Локализация не встроена в базовый пакет по умолчанию и подключается явно через модуль локалей, что делает поведение предсказуемым и независимым от среды выполнения.

Ключевая идея заключается в разделении:

  • модели времени (LocalDate, LocalTime, ZonedDateTime)
  • форматтера (DateTimeFormatter)
  • локали (Locale из расширения js-joda)

Такое разделение позволяет формировать строки представления даты в разных культурных форматах без изменения самих объектов времени.


Модель локали и подключение поддержки языков

Локаль в js-joda представляется объектом Locale, который приходит из пакета расширения локализации.

Основной пакет не содержит языковых данных (названия месяцев, дней недели), поэтому используется дополнительный модуль:

import { Locale } from '@js-joda/locale';

Локаль определяет:

  • язык (например, en, ru, de)
  • региональные особенности (например, формат даты, порядок элементов)
  • словари для месяцев и дней недели

Пример создания локали:

const ru = new Locale('ru');
const en = new Locale('en');
const de = new Locale('de');

DateTimeFormatter как основа форматирования

DateTimeFormatter отвечает за преобразование объектов времени в строку и обратно.

Импорт:

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

Форматтеры делятся на:

  • стандартные (ISO)
  • локализованные
  • пользовательские (pattern-based)

Форматирование без локали (ISO)

ISO-формат является базовым и не зависит от языка:

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

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

const iso = DateTimeFormatter.ISO_LOCAL_DATE;

console.log(date.format(iso)); 
// 2026-05-25

ISO-форматы всегда стабильны и используются для обмена данными между системами.


Локализованное форматирование

Для культурного представления даты используется локализованный форматтер.

Он создаётся через фабричный метод:

import { DateTimeFormatter, FormatStyle } from '@js-joda/core';
import { Locale } from '@js-joda/locale';

const formatter = DateTimeFormatter
  .ofLocalizedDate(FormatStyle.FULL)
  .withLocale(new Locale('ru'));

Пример применения:

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

console.log(date.format(formatter));
// понедельник, 25 мая 2026 г.

FormatStyle определяет уровень детализации:

  • SHORT — краткий формат
  • MEDIUM — средний
  • LONG — расширенный
  • FULL — полный с названием дня недели

Роль withLocale

withLocale не изменяет форматтер, а возвращает его копию с новой локалью.

const base = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);

const ruFormatter = base.withLocale(new Locale('ru'));
const enFormatter = base.withLocale(new Locale('en'));

Это важно, поскольку форматтеры в js-joda неизменяемые.


Локализованное время и дата-время

Локализация применяется не только к датам, но и к комплексным типам:

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

const dt = LocalDateTime.of(2026, 5, 25, 14, 30);

const formatter = DateTimeFormatter
  .ofLocalizedDateTime(FormatStyle.MEDIUM)
  .withLocale(new Locale('ru'));

console.log(dt.format(formatter));
// 25 мая 2026 г., 14:30:00

Для времени отдельно:

DateTimeFormatter
  .ofLocalizedTime(FormatStyle.SHORT)
  .withLocale(new Locale('ru'));

Пользовательские шаблоны и локаль

При использовании ofPattern локаль влияет только на текстовые элементы (месяцы, дни недели), но не на структуру паттерна.

const formatter = DateTimeFormatter
  .ofPattern('d MMMM yyyy')
  .withLocale(new Locale('ru'));
const date = LocalDate.of(2026, 5, 25);

console.log(date.format(formatter));
// 25 мая 2026

В английской локали:

const enFormatter = DateTimeFormatter
  .ofPattern('d MMMM yyyy')
  .withLocale(new Locale('en'));

console.log(date.format(enFormatter));
// 25 May 2026

Одно и то же выражение паттерна адаптируется под словарь локали.


Паттерны форматирования и локализация

Паттерны используют набор символов:

  • y — год
  • M — месяц
  • d — день
  • E — день недели
  • H — часы (24h)
  • h — часы (12h)
  • m — минуты
  • s — секунды

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

DateTimeFormatter
  .ofPattern('EEEE')
  .withLocale(new Locale('ru'));

Результат:

понедельник

В английской локали:

Monday

Влияние локали на месяцы и дни недели

Локаль управляет текстовыми компонентами:

Месяцы

DateTimeFormatter.ofPattern('MMMM')
  • ru → май
  • en → May
  • de → Mai

Дни недели

DateTimeFormatter.ofPattern('EEEE')
  • ru → понедельник
  • en → Monday
  • fr → lundi

Числовые части (день, год) не зависят от локали.


Парсинг строк с учётом локали

Локаль критична при обратном преобразовании строки в дату.

const formatter = DateTimeFormatter
  .ofPattern('d MMMM yyyy')
  .withLocale(new Locale('ru'));

const date = LocalDate.parse('25 мая 2026', formatter);

Без указания локали парсинг не сможет интерпретировать текстовые месяцы.


Ошибки при несоответствии локали

Если строка не совпадает с локалью форматтера:

const formatter = DateTimeFormatter
  .ofPattern('d MMMM yyyy')
  .withLocale(new Locale('en'));

LocalDate.parse('25 мая 2026', formatter);

возникает ошибка разбора, так как:

  • мая отсутствует в английском словаре месяцев

Сочетание локали и временных зон

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

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

const zdt = ZonedDateTime.now(ZoneId.of('Europe/Moscow'));

const formatter = DateTimeFormatter
  .ofPattern('EEEE, d MMMM yyyy HH:mm')
  .withLocale(new Locale('ru'));

console.log(zdt.format(formatter));

Здесь:

  • локаль отвечает за текст
  • зона отвечает за числовое время

Форматирование с несколькими локалями в приложении

Типичный сценарий — динамическая смена языка:

function formatDate(date, lang) {
  const locale = new Locale(lang);

  const formatter = DateTimeFormatter
    .ofPattern('d MMMM yyyy')
    .withLocale(locale);

  return date.format(formatter);
}

Примеры:

  • ru → 25 мая 2026
  • en → 25 May 2026
  • de → 25 Mai 2026

Отличие от Intl.DateTimeFormat

Встроенный Intl.DateTimeFormat:

new Intl.DateTimeFormat('ru-RU', { dateStyle: 'full' })

js-joda:

DateTimeFormatter
  .ofLocalizedDate(FormatStyle.FULL)
  .withLocale(new Locale('ru'))

Различия:

  • js-joda использует строгую модель времени без неявных преобразований
  • локаль подключается явно
  • поведение одинаково вне зависимости от среды (Node, браузер)

Поведение локали в различных форматах

Локаль влияет только на:

  • названия месяцев
  • названия дней недели
  • форматы предустановленных стилей (FULL, LONG, и т.д.)

Не влияет на:

  • числовые значения даты
  • порядок хранения полей внутри объекта
  • арифметику дат

Использование нескольких локалей для одного форматтера

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

const base = DateTimeFormatter.ofPattern('d MMMM yyyy');

const formats = {
  ru: base.withLocale(new Locale('ru')),
  en: base.withLocale(new Locale('en')),
  de: base.withLocale(new Locale('de')),
};

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


Нестандартные случаи локализации

При работе с редкими локалями возможны ограничения:

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

js-joda в таких случаях либо использует fallback на английский, либо возвращает техническое представление без локализации текста.


Форматирование времени суток и 12/24-часовой режим

Локаль может влиять на выбор стандартного отображения времени в FormatStyle, но не изменяет сам формат символов:

DateTimeFormatter
  .ofLocalizedTime(FormatStyle.SHORT)
  .withLocale(new Locale('en'));

В некоторых локалях:

  • предпочтителен 24-часовой формат (ru, de)
  • 12-часовой формат (en-US)

Итоговые особенности поведения локали в js-joda

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