Основы работы с датами

Работа с датами в экосистеме FormatJS строится вокруг стандарта ECMA-402 (Intl API) и абстракции над ним, предоставляемой библиотеками семейства, такими как react-intl и низкоуровневые утилиты форматирования. Основная цель — унифицированное представление дат в разных локалях без ручного управления форматами строк.


Представление даты как универсальной сущности

В JavaScript дата изначально представлена объектом Date, который хранит количество миллисекунд с 1 января 1970 года (UTC). Однако отображение даты пользователю зависит от:

  • локали (ru, en, de и т. д.)
  • часового пояса
  • контекста отображения (краткий формат, полный, относительный)
  • культурных соглашений

FormatJS опирается на Intl.DateTimeFormat, расширяя и стандартизируя его использование.


Базовое форматирование даты через Intl

Низкоуровневый механизм, который используется внутри FormatJS:

const date = new Date('2026-05-29T12:00:00Z');

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

formatter.format(date);

Результат зависит от локали:

29 мая 2026 г.

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


Форматирование дат в react-intl

Основной способ работы с датами в приложениях на FormatJS — использование react-intl.

Компонент FormattedDate инкапсулирует Intl.DateTimeFormat:

import { FormattedDate } from 'react-intl';

function Example() {
  const date = new Date('2026-05-29T12:00:00Z');

  return (
    <FormattedDate
      value={date}
      year="numeric"
      month="long"
      day="2-digit"
    />
  );
}

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


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

Для более гибкого управления используется хук useIntl, предоставляющий доступ к методам форматирования.

import { useIntl } from 'react-intl';

function Example() {
  const intl = useIntl();

  const date = new Date('2026-05-29T12:00:00Z');

  const formattedDate = intl.formatDate(date, {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  });

  return <div>{formattedDate}</div>;
}

Преимущество этого подхода — возможность динамического формирования формата в логике приложения.


Управление локалью

FormatJS полностью зависит от контекста IntlProvider, который определяет локаль:

import { IntlProvider } from 'react-intl';

<IntlProvider locale="ru">
  <App />
</IntlProvider>

Изменение локали автоматически влияет на форматирование всех дат внутри дерева компонентов.


Форматы дат: предустановки и кастомизация

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

  • short
  • medium
  • long
  • full
intl.formatDate(date, {
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  format: 'long'
});

Пример поведения:

  • short → 29.05.26
  • medium → 29 мая 2026 г.
  • long → 29 мая 2026 года
  • full → пятница, 29 мая 2026 года

Эти стили соответствуют стандартам Intl.DateTimeFormat и могут быть переопределены через IntlProvider.


Работа с временем и объединёнными форматами

Date в FormatJS может включать время:

intl.formatDate(date, {
  year: 'numeric',
  month: '2-digit',
  day: '2-digit',
  hour: '2-digit',
  minute: '2-digit'
});

Результат зависит от локали и часового пояса:

29.05.2026, 15:00

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


Часовые пояса и их влияние

FormatJS не заменяет Intl, поэтому управление временем осуществляется через стандартный параметр timeZone:

intl.formatDate(date, {
  timeZone: 'UTC',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
});

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


Форматирование в сообщениях ICU

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

intl.formatMessage(
  {
    id: 'event.date',
    defaultMessage: 'Событие состоится {date}'
  },
  {
    date: intl.formatDate(date, {
      year: 'numeric',
      month: 'long',
      day: 'numeric'
    })
  }
);

Такой подход обеспечивает отделение текста от локализованных значений дат.


Относительные даты и их роль

FormatJS поддерживает относительное форматирование через formatRelativeTime (в зависимости от версии пакета):

intl.formatRelativeTime(-1, 'day');

Результат:

вчера

или

2 дня назад

Относительные даты особенно важны для интерфейсов с динамическими событиями, лентами и уведомлениями.


Нормализация входных данных

FormatJS ожидает корректные входные типы:

  • Date
  • timestamp (число)
  • ISO string (в некоторых реализациях)

Рекомендуется нормализовать данные до объекта Date:

const date = new Date(apiResponse.createdAt);

Ошибки в формате даты приводят к Invalid Date, что ломает форматирование.


Переиспользуемые форматы

Для масштабируемых приложений применяются централизованные форматы:

const messages = defineMessages({
  dateShort: {
    id: 'date.short',
    defaultMessage: '{date, date, short}'
  }
});

И использование:

intl.formatMessage(messages.dateShort, {
  date: new Date()
});

Это снижает дублирование конфигураций и упрощает поддержку локализации.


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

Если IntlProvider не задан, FormatJS использует локаль браузера:

navigator.language

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


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

Форматирование дат через Intl.DateTimeFormat является дорогостоящей операцией при массовом рендеринге. FormatJS учитывает это и позволяет:

  • кешировать форматтеры
  • переиспользовать инстансы Intl
  • избегать повторного создания объектов

Пример оптимизированного подхода:

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

formatter.format(date1);
formatter.format(date2);

В рамках FormatJS это скрыто внутри библиотек, но важно для понимания поведения системы.


Особенности работы с сериализацией

При серверном рендеринге (SSR) даты часто сериализуются в строки:

"2026-05-29T12:00:00Z"

На клиенте требуется восстановление:

new Date(serializedDate);

FormatJS не хранит состояние даты, а только форматирует её представление, поэтому корректность восстановления данных определяет итоговый результат отображения.