Форматы времени

Основы представления времени через Intl API

В основе работы FormatJS с временем лежит стандарт ECMAScript Internationalization API, а именно Intl.DateTimeFormat. Этот механизм обеспечивает локализованное форматирование даты и времени без ручной обработки строковых шаблонов.

Формирование времени опирается на набор параметров:

  • hour — отображение часа
  • minute — отображение минут
  • second — отображение секунд
  • hour12 — 12- или 24-часовой формат
  • timeZone — временная зона
  • timeZoneName — отображение названия временной зоны

Пример базового использования:

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

formatter.format(new Date());

FormatJS не заменяет этот механизм, а предоставляет более декларативный слой поверх него, особенно в контексте React и ICU-форматов.


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

В экосистеме FormatJS ключевую роль играет библиотека react-intl, которая предоставляет компонентный и императивный API для работы с локализованным временем.

Компонент FormattedTime

FormattedTime используется для декларативного отображения времени в React-компонентах:

import { FormattedTime } from 'react-intl';

<FormattedTime value={new Date()} />

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

Расширенная конфигурация:

<FormattedTime
  value={new Date()}
  hour="2-digit"
  minute="2-digit"
  second="2-digit"
/>

Компонент автоматически адаптируется под локаль пользователя, включая порядок элементов и формат 12/24 часа.


Императивное форматирование времени

Помимо компонентов, FormatJS предоставляет API через хук useIntl:

import { useIntl } from 'react-intl';

const Component = () => {
  const intl = useIntl();

  const timeString = intl.formatTime(new Date(), {
    hour: '2-digit',
    minute: '2-digit'
  });

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

Метод formatTime является обёрткой над Intl.DateTimeFormat, но учитывает контекст локализации приложения, заданный через провайдер.


Предопределённые форматы времени

FormatJS позволяет задавать именованные форматы, что упрощает единообразное отображение времени в больших приложениях.

Конфигурация задаётся через IntlProvider:

import { IntlProvider } from 'react-intl';

const messages = {};

const formats = {
  time: {
    short: {
      hour: '2-digit',
      minute: '2-digit'
    },
    medium: {
      hour: '2-digit',
      minute: '2-digit',
      second: '2-digit'
    },
    long: {
      hour: '2-digit',
      minute: '2-digit',
      second: '2-digit',
      timeZoneName: 'short'
    }
  }
};

<IntlProvider locale="ru-RU" messages={messages} formats={formats}>
  <App />
</IntlProvider>;

Использование именованного формата:

<FormattedTime value={new Date()} format="short" />

Или через API:

intl.formatTime(date, { format: 'short' });

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


Часовые пояса и их влияние на форматирование

Работа с временными зонами является критически важной частью локализации. FormatJS позволяет явно задавать timeZone:

intl.formatTime(new Date(), {
  hour: '2-digit',
  minute: '2-digit',
  timeZone: 'Europe/Moscow'
});

При отсутствии явного указания используется временная зона окружения выполнения (браузера или Node.js).

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

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

Форматы 12/24 часа

Одним из ключевых аспектов локализации времени является выбор между 12- и 24-часовой системой.

intl.formatTime(date, {
  hour: '2-digit',
  minute: '2-digit',
  hour12: true
});

или

intl.formatTime(date, {
  hour: '2-digit',
  minute: '2-digit',
  hour12: false
});

В большинстве случаев hour12 не задаётся явно, так как система определяется локалью:

  • en-US → 12-часовой формат
  • ru-RU, de-DE, fr-FR → 24-часовой формат

FormatJS следует этим правилам автоматически через ICU.


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

Поддержка секунд зависит от требований интерфейса:

intl.formatTime(date, {
  hour: '2-digit',
  minute: '2-digit',
  second: '2-digit'
});

Хотя Intl.DateTimeFormat не всегда гарантирует поддержку миллисекунд, некоторые окружения позволяют расширенные настройки через fractionalSecondDigits:

intl.formatTime(date, {
  hour: '2-digit',
  minute: '2-digit',
  second: '2-digit',
  fractionalSecondDigits: 3
});

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


Форматирование времени в разных локалях

FormatJS полностью полагается на ICU-правила локализации. Это означает, что формат времени может кардинально меняться в зависимости от языка:

  • порядок компонентов времени
  • разделители (:, ., h)
  • наличие AM/PM
  • написание временной зоны

Пример:

intl.formatTime(date);

Результаты:

  • en-US3:45 PM
  • ru-RU15:45
  • fr-FR15:45
  • en-GB15:45

Логика форматирования не зашивается в приложение, а делегируется ICU-данным.


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

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

intl.formatTimeToParts(date);

Результат:

[
  { type: 'hour', value: '15' },
  { type: 'literal', value: ':' },
  { type: 'minute', value: '30' }
]

Это позволяет:

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

Интеграция с SSR (Server-Side Rendering)

При серверном рендеринге время может становиться источником несоответствий между сервером и клиентом.

Причины:

  • различие временных зон
  • различие локали окружения
  • различие системных настроек

Для устранения расхождений фиксируются:

  • locale в IntlProvider
  • timeZone в форматирующих функциях
  • единый источник даты (например, ISO-строка)

Пример стабилизации:

<IntlProvider locale="ru-RU" timeZone="Europe/Moscow">
  <App />
</IntlProvider>

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

Каждый вызов Intl.DateTimeFormat может быть дорогим при массовом использовании. FormatJS оптимизирует этот процесс за счёт кэширования форматтеров внутри intl-контекста.

Рекомендации:

  • избегать создания новых Intl.DateTimeFormat вручную в рендере;
  • использовать intl.formatTime вместо прямого API;
  • применять именованные форматы для переиспользования конфигураций;
  • минимизировать динамические пересоздания formats в IntlProvider.

Сравнение подходов форматирования времени

Разные способы форматирования времени в FormatJS решают разные задачи:

Подход Назначение
<FormattedTime /> декларативный React-рендер
intl.formatTime императивная логика
formats.time централизованные стили
formatToParts кастомный рендеринг
Intl.DateTimeFormat низкоуровневый API

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


Работа с динамическим временем

FormatJS не отслеживает изменение времени автоматически. Для отображения “живого” времени используется внешнее обновление состояния:

const [now, setNow] = useState(new Date());

useEffect(() => {
  const id = setInterval(() => setNow(new Date()), 1000);
  return () => clearInterval(id);
}, []);

Далее значение передаётся в FormattedTime или formatTime.


Особенности обработки некорректных значений

При передаче невалидного значения Date поведение зависит от реализации:

  • Invalid Date может привести к ошибке форматирования;
  • null и undefined требуют явной обработки;
  • строки автоматически не парсятся.

Корректный подход:

const safeDate = value ? new Date(value) : null;

Стабильность отображения времени в сложных интерфейсах

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

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

Для консистентности применяются единые правила:

  • фиксированный формат (short, medium)
  • единая временная зона
  • использование ICU-локализации без ручных преобразований
  • минимизация кастомных строковых шаблонов