Единицы времени

В экосистеме FormatJS работа с временем строится вокруг стандарта ICU (International Components for Unicode), где временные интервалы выражаются через абстрактные единицы: секунды, минуты, часы, дни, недели, месяцы и годы. Такая модель позволяет унифицировать локализацию относительного времени и формирование сообщений, зависящих от грамматических правил языка.

Ключевая роль принадлежит Intl.RelativeTimeFormat — компоненту, отвечающему за преобразование числовых значений и единиц времени в локализованные строки. В связке с intl-messageformat и ICU Message Syntax формируется единая система представления временных данных.


Базовая модель единиц времени

В FormatJS единицы времени задаются строковыми идентификаторами, соответствующими стандарту:

  • second — секунды
  • minute — минуты
  • hour — часы
  • day — дни
  • week — недели
  • month — месяцы
  • quarter — кварталы
  • year — годы

Каждая единица интерпретируется не как фиксированное количество секунд, а как семантическая категория, зависящая от локали и правил округления.

Например, «месяц» в одном языке может влиять на падежную систему иначе, чем в другом, даже при одинаковой числовой логике.


Intl.RelativeTimeFormat и единицы времени

Основной механизм FormatJS для работы с единицами времени реализуется через Intl.RelativeTimeFormat.

Принцип работы основан на паре:

  • числовое значение (delta)
  • единица времени (unit)

Пример базовой структуры:

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

rtf.format(-1, 'day');
rtf.format(1, 'hour');
rtf.format(-5, 'minute');

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


Система знаков и направление времени

В FormatJS знак числа определяет направление временного сдвига:

  • отрицательные значения — прошедшее время
  • положительные значения — будущее время
Значение Единица Интерпретация
-1 day 1 день назад
1 day через 1 день
-3 hour 3 часа назад
2 week через 2 недели

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


Округление и нормализация единиц

При работе с реальными временными данными часто требуется нормализация значений. FormatJS не выполняет автоматическое преобразование между единицами — ответственность за выбор единицы лежит на уровне бизнес-логики.

Типичные стратегии округления:

  • секунды → минуты при значениях ≥ 60
  • минуты → часы при значениях ≥ 60
  • часы → дни при значениях ≥ 24
  • дни → недели при значениях ≥ 7
  • месяцы → годы при значениях ≥ 12

Пример нормализации:

function normalize(value, unit) {
  if (unit === 'second' && value >= 60) {
    return { value: Math.round(value / 60), unit: 'minute' };
  }
  if (unit === 'minute' && value >= 60) {
    return { value: Math.round(value / 60), unit: 'hour' };
  }
  return { value, unit };
}

ICU Message Syntax и временные единицы

В intl-messageformat единицы времени часто используются внутри ICU-шаблонов, особенно при построении динамических сообщений.

Пример структуры:

const message = new IntlMessageFormat(
  '{count, plural, one {# минута назад} few {# минуты назад} many {# минут назад} other {# минут}}',
  'ru'
);

Хотя здесь напрямую не используется RelativeTimeFormat, семантика единиц времени сохраняется через plural rules, которые тесно связаны с временными интервалами.


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

Каждая единица времени в FormatJS подчиняется правилам конкретной локали:

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

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

const rtfEn = new Intl.RelativeTimeFormat('en');
const rtfRu = new Intl.RelativeTimeFormat('ru');

rtfEn.format(-1, 'day'); // "1 day ago"
rtfRu.format(-1, 'day'); // "1 день назад"

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


Стабильные и нестабильные единицы

Единицы времени можно условно разделить на две категории:

Мелкие единицы

  • second
  • minute
  • hour

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

Крупные единицы

  • day
  • week
  • month
  • year

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


Согласование единиц времени в интерфейсах

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

Типичная стратегия выбора:

  • < 60 секунд → seconds
  • < 60 минут → minutes
  • < 24 часа → hours
  • < 7 дней → days
  • < 30 дней → weeks/months
  • 30 дней → months/years


Форматирование с опцией numeric

Параметр numeric в RelativeTimeFormat влияет на стиль вывода:

  • numeric: "always" — всегда числовая форма
  • numeric: "auto" — допускает словесные формы

Пример:

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

rtf.format(-1, 'day'); // "yesterday"
rtf.format(1, 'day');  // "tomorrow"
rtf.format(-2, 'day'); // "2 days ago"

Автоматические формы зависят от поддержки локали и набора правил ICU.


Кастомные единицы и расширение модели

Несмотря на фиксированный набор стандартных единиц, в прикладной логике часто вводятся абстракции:

  • business-day
  • fiscal-quarter
  • academic-year

FormatJS не интерпретирует такие единицы напрямую, но допускает их маппинг к базовым:

const mapUnit = (unit) => {
  switch (unit) {
    case 'business-day': return 'day';
    case 'fiscal-quarter': return 'quarter';
    default: return unit;
  }
};

Переполнение единиц и цепочки преобразований

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

  • 120 секунд → 2 минуты
  • 90 минут → 1.5 часа
  • 26 часов → 1 день + 2 часа

FormatJS не выполняет подобные операции, однако структура единиц времени в ICU предполагает возможность внешнего расчёта и последующего форматирования только финального результата.


Связь единиц времени с plural rules

Единицы времени тесно связаны с системой множественного числа ICU:

  • one minute
  • few minutes
  • many minutes

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


Поведение при граничных значениях

Граничные значения (0, 1, -1) имеют особое значение:

  • 0 часто трактуется как «сейчас»
  • 1 и -1 — базовые формы единицы
  • значения около 0 могут иметь специфические локальные формы

Пример:

const rtf = new Intl.RelativeTimeFormat('en');

rtf.format(0, 'day'); // "in 0 days"

Некоторые локали переопределяют поведение нуля через numeric: "auto".


Временные единицы в архитектуре сообщений

В системах на FormatJS единицы времени обычно отделены от слоя сообщений:

  1. вычисление дельты времени
  2. выбор единицы
  3. передача в RelativeTimeFormat
  4. локализация результата

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