Относительное время — один из ключевых элементов интерфейсов,
ориентированных на динамические данные: ленты новостей, комментарии,
уведомления, мессенджеры, журналы событий. Библиотека FormatJS
предоставляет стандартизированный слой для работы с локализованным
представлением времени через API, основанный на стандарте ECMAScript
Internationalization API — Intl.RelativeTimeFormat.
Относительное время выражает разницу между двумя моментами не в абсолютных значениях (дата и время), а в человеческих понятиях:
В основе лежит объект Intl.RelativeTimeFormat, который
предоставляет форматирование с учётом языка и локали.
В контексте FormatJS используется полифилл
@formatjs/intl-relativetimeformat, обеспечивающий
одинаковое поведение в разных окружениях.
Форматирование опирается на две сущности:
Пример логики:
-1 minute → «1 минуту назад»5 minutes → «через 5 минут»Знак числа определяет направление времени:
Стандартный API Jav * aScript:
const rtf = new Intl.RelativeTimeFormat('ru', { numeric: 'auto' });
rtf.format(-1, 'minute'); // "минуту назад"
rtf.format(5, 'minute'); // "через 5 минут"
Параметр numeric: 'auto' позволяет использовать
естественные формы языка:
В FormatJS используется пакет:
@formatjs/intl-relativetimeformat
Установка:
npm install @formatjs/intl-relativetimeformat
Инициализация polyfill:
import '@formatjs/intl-relativetimeformat/polyfill';
import '@formatjs/intl-relativetimeformat/locale-data/ru';
После подключения поведение Intl.RelativeTimeFormat
становится предсказуемым во всех средах.
Для отображения относительного времени требуется преобразование абсолютной даты в разницу относительно текущего времени.
Пример базовой функции:
const rtf = new Intl.RelativeTimeFormat('ru', { numeric: 'auto' });
function formatRelative(date) {
const now = Date.now();
const diff = date - now;
const seconds = Math.round(diff / 1000);
const minutes = Math.round(seconds / 60);
const hours = Math.round(minutes / 60);
const days = Math.round(hours / 24);
if (Math.abs(days) > 0) return rtf.format(days, 'day');
if (Math.abs(hours) > 0) return rtf.format(hours, 'hour');
if (Math.abs(minutes) > 0) return rtf.format(minutes, 'minute');
return rtf.format(seconds, 'second');
}
Корректное отображение требует адаптивного выбора единицы:
Оптимизация повышает читаемость и снижает когнитивную нагрузку.
Intl.RelativeTimeFormat учитывает:
Пример различий:
new Intl.RelativeTimeFormat('en').format(-1, 'day'); // "1 day ago"
new Intl.RelativeTimeFormat('ru').format(-1, 'day'); // "вчера"
Некоторые языки используют специальные слова вместо числовых форм при
numeric: 'auto'.
Параметр style управляет детализацией:
new Intl.RelativeTimeFormat('ru', { style: 'long' });
new Intl.RelativeTimeFormat('ru', { style: 'short' });
new Intl.RelativeTimeFormat('ru', { style: 'narrow' });
Различия:
long → «через 5 минут»short → «через 5 мин.»narrow → «+5 мин»Выбор стиля зависит от плотности интерфейса.
Создание экземпляра Intl.RelativeTimeFormat относительно
дорогое, поэтому применяется кэширование:
const cache = new Map();
function getFormatter(locale) {
if (!cache.has(locale)) {
cache.set(locale, new Intl.RelativeTimeFormat(locale, { numeric: 'auto' }));
}
return cache.get(locale);
}
Это особенно важно в SPA и высоконагруженных интерфейсах.
При переходах между единицами возникают пограничные эффекты:
Решение заключается в строгом округлении:
const seconds = Math.round(diff / 1000);
Альтернативой является Math.floor, но он может создавать
задержку обновления текста.
Относительное время используется в динамических интерфейсах, но имеет ограничения:
Абсолютное время предпочтительно для:
Intl.RelativeTimeFormat поддерживает большие
значения:
rtf.format(-3, 'month'); // "3 месяца назад"
rtf.format(2, 'year'); // "через 2 года"
Однако при больших промежутках интерфейсы часто переходят на абсолютные даты.
Относительное время требует периодического обновления отображения:
Пример стратегии:
setInterval(updateRelativeTimes, 60 * 1000);
Более точный подход — динамическое вычисление следующего обновления на основе текущей разницы.
Поддержка локалей критична для корректного отображения:
new Intl.RelativeTimeFormat('fr');
new Intl.RelativeTimeFormat('de');
new Intl.RelativeTimeFormat('zh');
new Intl.RelativeTimeFormat('ru');
FormatJS использует набор локализационных данных, которые расширяются
через отдельные пакеты locale-data.
В компонентных системах функция форматирования выносится в утилиту:
export function createRelativeTime(locale) {
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
return (date) => {
const diff = date - Date.now();
const seconds = Math.round(diff / 1000);
const minutes = Math.round(seconds / 60);
if (Math.abs(minutes) < 60) {
return rtf.format(minutes, 'minute');
}
const hours = Math.round(minutes / 60);
if (Math.abs(hours) < 24) {
return rtf.format(hours, 'hour');
}
const days = Math.round(hours / 24);
return rtf.format(days, 'day');
};
}
В SSR-окружениях важно:
Polyfill FormatJS обеспечивает одинаковый результат рендеринга между сервером и браузером.
При отсутствии нативной поддержки:
@formatjs/intl-relativetimeformatЭто позволяет сохранять единое поведение интерфейса без деградации UX.