Концепция относительного форматирования

Относительное форматирование представляет собой способ отображения времени и величин не в абсолютных значениях, а в контексте текущего момента или другой опорной точки. Вместо фиксированных дат и чисел используются выражения вида «2 минуты назад», «через 3 дня», «прошлой неделе», «в следующем году». Такой подход является частью международных стандартов локализации и широко применяется в пользовательских интерфейсах, где важна мгновенная интерпретация информации без когнитивной нагрузки на преобразование дат.

В экосистеме FormatJS относительное форматирование реализуется как надстройка над стандартным API JavaScript Intl.RelativeTimeFormat, а также интегрируется в механизмы форматирования сообщений ICU MessageFormat через react-intl и связанные пакеты.


Семантическая основа относительного времени

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

Ключевые элементы модели:

  • Базовая точка отсчёта (обычно текущее время)
  • Целевая дата или событие
  • Разница между ними
  • Единица измерения (unit): секунды, минуты, часы, дни, недели, месяцы, годы

Формально значение можно представить как:

delta = targetTime - baseTime

Далее delta нормализуется к выбранной единице измерения.


Механизм преобразования интервала

Относительное форматирование не просто отображает разницу в миллисекундах, а выбирает наиболее подходящую единицу.

Типичный процесс:

  1. Вычисление разницы в миллисекундах

  2. Последовательное деление на коэффициенты:

    • 1000 → секунды
    • 60 → минуты
    • 60 → часы
    • 24 → дни
    • 7 → недели (условно)
    • 30/31 → месяцы (приближённо)
    • 12 → годы
  3. Выбор наибольшей единицы, дающей «разумное» число

Пример логики:

  • 45 секунд → «45 секунд назад»
  • 90 секунд → «1 минута назад»
  • 3600 секунд → «1 час назад»

API Intl.RelativeTimeFormat

Базовый механизм, на котором строится относительное форматирование в Jav * aScript:

const rtf = new Intl.RelativeTimeFormat('ru', { numeric: 'auto' });

rtf.format(-1, 'day');   // "вчера"
rtf.format(-5, 'day');   // "5 дней назад"
rtf.format(2, 'hour');   // "через 2 часа"

Основные параметры:

  • locale — язык и регион (ru, en-US, de)

  • options.numeric

    • always — всегда числовой формат («1 день назад»)
    • auto — допускает слова «вчера», «сегодня», «завтра»
  • unit — единица времени


Поддержка в FormatJS

В экосистеме FormatJS относительное форматирование интегрируется через несколько уровней:

1. react-intl и форматирование сообщений

react-intl позволяет использовать относительное форматирование прямо в UI-строках:

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

или через декларативные сообщения:

<FormattedRelativeTime value={-2} unit="hour" />

2. ICU MessageFormat и встроенные функции

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

{value, relative, unit: "day"}

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


Относительные единицы измерения

Поддерживаемые единицы строго определены спецификацией Intl.RelativeTimeFormat:

  • second
  • minute
  • hour
  • day
  • week
  • month
  • quarter
  • year

Каждая единица имеет собственные правила склонения и пороговые значения в зависимости от локали.


Лингвистическая локализация

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

Например, в русском языке:

  • 1 минута → «1 минута назад»
  • 2–4 минуты → «2 минуты назад»
  • 5+ минут → «5 минут назад»

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

  • 1 minute ago
  • 2 minutes ago

FormatJS опирается на CLDR (Unicode Common Locale Data Repository), который содержит правила:

  • множественного числа
  • падежей
  • грамматических форм времени

Параметр numeric: auto vs always

Выбор режима влияет на выразительность интерфейса.

numeric: always

Всегда используется числовая форма:

rtf.format(-1, 'day'); // "1 day ago"

Поведение строго и предсказуемо.

numeric: auto

Используются естественные выражения:

rtf.format(-1, 'day'); // "yesterday"
rtf.format(0, 'day');  // "today"
rtf.format(1, 'day');  // "tomorrow"

Этот режим улучшает читаемость интерфейсов, ориентированных на человека.


Пороговые переходы между единицами

Относительное форматирование включает внутренние правила переключения единиц.

Примеры типичных переходов:

  • 59 секунд → «59 секунд назад»
  • 60 секунд → «1 минута назад»
  • 59 минут → «59 минут назад»
  • 60 минут → «1 час назад»

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


Интеграция с React-компонентами

В контексте FormatJS относительное форматирование часто используется в UI-слое:

import { FormattedRelativeTime } from 'react-intl';

function CommentTime({ value }) {
  return (
    <FormattedRelativeTime value={value} unit="minute" />
  );
}

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

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

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

Относительное форматирование требует строгой нормализации:

  • отрицательные значения → прошедшее время
  • положительные значения → будущее время
  • ноль → текущий момент (зависит от локали)

Пример:

formatRelativeTime(-10, 'minute'); // 10 минут назад
formatRelativeTime(10, 'minute');  // через 10 минут

Производительность и кеширование

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

  • кеширование экземпляров Intl.RelativeTimeFormat
  • переиспользование локалей
  • минимизация пересчётов при рендере
  • обновление по таймеру только при смене единицы (например, минута → час)

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

const rtfCache = new Map();

function getRtf(locale) {
  if (!rtfCache.has(locale)) {
    rtfCache.set(locale, new Intl.RelativeTimeFormat(locale));
  }
  return rtfCache.get(locale);
}

Связь с абсолютным форматированием

Относительное форматирование не заменяет абсолютное, а дополняет его.

Сравнение:

  • Абсолютное: «29 мая 2026, 14:30»
  • Относительное: «2 часа назад»

Выбор зависит от контекста:

  • ленты событий → относительное
  • архивы → абсолютное
  • отчёты → абсолютное с возможным дополнением относительного

Пограничные случаи и неоднозначности

Относительное форматирование сталкивается с рядом краевых ситуаций:

  • очень большие интервалы (десятки лет)
  • погрешности округления
  • изменение системного времени
  • переходы часовых поясов
  • DST (летнее/зимнее время)

В таких случаях FormatJS делегирует поведение Intl.RelativeTimeFormat, не выполняя дополнительной семантической интерпретации.


Модель округления значений

При преобразовании чисел используется округление, обычно к ближайшему целому:

  • 1.2 дня → 1 день
  • 1.7 дня → 2 дня

В некоторых реализациях возможны стратегии:

  • floor (округление вниз)
  • ceil (округление вверх)
  • round (математическое округление)

FormatJS следует поведению стандарта ECMAScript, где округление определяется спецификацией API.


Использование в потоковых интерфейсах

Относительное форматирование особенно эффективно в динамических системах:

  • чаты
  • социальные ленты
  • системы уведомлений
  • логирование событий

Причина — естественная интерпретация времени без необходимости точного вычисления даты.


Комбинирование с другими форматами

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

  • числа + относительное время: «3 новых сообщения · 5 минут назад»
  • даты + относительные метки: «Опубликовано: вчера, 18:00»
  • события + длительность: «активен 2 часа»

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


Ограничения модели

Несмотря на универсальность, относительное форматирование имеет ограничения:

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

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