Комбинированные форматы даты и времени

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


Базовая модель комбинирования в Intl.DateTimeFormat

Основой всех операций форматирования служит Intl.DateTimeFormat, который принимает набор опций, разделённых на две группы:

  • параметры даты (year, month, day, weekday)
  • параметры времени (hour, minute, second, hour12, timeZoneName)

Комбинированный формат формируется простым объединением этих групп в одном объекте конфигурации:

const formatter = new Intl.DateTimeFormat('ru-RU', {
  year: 'numeric',
  month: 'long',
  day: '2-digit',
  hour: '2-digit',
  minute: '2-digit'
});

formatter.format(new Date());

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


Принцип объединения date и time частей

Комбинированный формат не является отдельным типом — он строится из двух независимых подсистем:

  • Date components: календарное представление
  • Time components: представление времени суток

При объединении действует единый механизм разрешения конфликтов:

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

Например:

new Intl.DateTimeFormat('en-US', {
  dateStyle: 'full',
  timeStyle: 'short'
}).format(new Date());

Использование dateStyle и timeStyle упрощает создание комбинированных форматов без ручного перечисления полей.


DateStyle и TimeStyle как высокоуровневый слой

Современные реализации Intl поддерживают абстракции:

  • dateStyle: "full" | "long" | "medium" | "short"
  • timeStyle: "full" | "long" | "medium" | "short"

Комбинация этих параметров создаёт предсказуемый локализованный формат:

const fmt = new Intl.DateTimeFormat('fr-FR', {
  dateStyle: 'long',
  timeStyle: 'short'
});

fmt.format(new Date());

Эти параметры исключают необходимость вручную комбинировать year, month, day, hour и другие поля.


Комбинированные форматы в React-интеграции FormatJS

В составе FormatJS (через react-intl) комбинированные форматы реализуются через компоненты и API:

  • <FormattedDate>
  • <FormattedTime>
  • <FormattedDateTime>
  • formatDate
  • formatTime
  • formatDateTime

Компонент <FormattedDateTime> объединяет две подсистемы форматирования:

<FormattedDateTime
  value={new Date()}
  year="numeric"
  month="long"
  day="2-digit"
  hour="2-digit"
  minute="2-digit"
/>

Внутри происходит делегирование в Intl.DateTimeFormat, где параметры даты и времени агрегируются в единый конфиг.


Механика formatDateTime

Функция formatDateTime выполняет нормализацию входных параметров:

  1. выделяет date-опции
  2. выделяет time-опции
  3. объединяет их в один объект
  4. передаёт в Intl.DateTimeFormat

Схематически:

formatDateTime(value, options) {
  const dateOptions = extractDate(options);
  const timeOptions = extractTime(options);

  return new Intl.DateTimeFormat(locale, {
    ...dateOptions,
    ...timeOptions
  }).format(value);
}

Конфликты между опциями разрешаются по приоритету времени, если параметры перекрываются.


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

Комбинированные форматы полностью зависят от локали:

  • в некоторых локалях дата предшествует времени
  • в других — наоборот
  • разделители могут отсутствовать или заменяться символами типа «в», «at», «,»

Пример различий:

  • ru-RU: 29 мая 2026 г., 14:30
  • en-US: May 29, 2026, 2:30 PM
  • de-DE: 29. Mai 2026, 14:30

Эти правила не задаются в коде и не подлежат ручной фиксации без необходимости.


Форматирование через formatToParts

Для точного контроля над комбинированными форматами используется formatToParts, возвращающий структурированный массив:

const parts = new Intl.DateTimeFormat('en-US', {
  year: 'numeric',
  month: 'short',
  day: '2-digit',
  hour: '2-digit',
  minute: '2-digit'
}).formatToParts(new Date());

Результат содержит элементы:

  • year
  • month
  • day
  • hour
  • minute
  • literal

Такой подход позволяет:

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

TimeZone и влияние на комбинированные форматы

При объединении даты и времени критическую роль играет timeZone:

new Intl.DateTimeFormat('ru-RU', {
  dateStyle: 'medium',
  timeStyle: 'short',
  timeZone: 'Europe/Moscow'
});

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

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

Комбинированный формат всегда интерпретирует момент времени как единый timestamp.


Поведение при частичном задании опций

Комбинированный формат допускает смешение:

  • dateStyle + timeStyle (высокоуровневый режим)
  • ручные date/time поля (низкоуровневый режим)
  • смешение не рекомендуется при конфликте параметров

Если заданы оба стиля и отдельные поля, приоритет имеют explicit-опции:

{
  dateStyle: 'long',
  timeStyle: 'short',
  month: 'numeric'
}

В таком случае month: 'numeric' переопределяет часть dateStyle.


ICU-совместимость и шаблоны комбинирования

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

const message = `
  {date, date, long} в {date, time, short}
`;

Здесь:

  • date, date отвечает за календарную часть
  • date, time отвечает за временную часть
  • форматирование выполняется отдельно и затем объединяется в строку

Такой подход особенно важен для динамических интерфейсов и уведомлений.


Типичные паттерны комбинированных форматов

На практике выделяются устойчивые паттерны:

Полный формат

  • дата + время с максимальной детализацией

Сокращённый формат

  • дата + краткое время

Событийный формат

  • дата + время начала события

Локализованный минимализм

  • опущены секунды и год

Поведение при отсутствии времени

Если задан только набор date-опций и отсутствуют time-опции, комбинирование не происходит. Аналогично:

  • только dateStyle → дата
  • только timeStyle → время
  • оба → комбинированный формат

Внутренние особенности реализации FormatJS

Внутренне FormatJS:

  • нормализует входные параметры
  • унифицирует API поверх Intl
  • обеспечивает fallback для окружений без полной поддержки dateStyle/timeStyle
  • кэширует форматтеры для повышения производительности

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


Поведение при изменении локали

Изменение locale приводит к пересозданию форматтера:

  • изменяется порядок date/time частей
  • изменяются разделители
  • может меняться формат времени (12/24 часа)
new Intl.DateTimeFormat('ja-JP', {
  dateStyle: 'long',
  timeStyle: 'short'
});

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


Оптимизация комбинированного форматирования

Для высоконагруженных приложений применяется:

  • переиспользование экземпляров Intl.DateTimeFormat
  • мемоизация в FormatJS
  • предрасчёт форматтеров на уровне провайдера локали

Это снижает стоимость многократного форматирования в UI-компонентах.