Нативные возможности JavaScript

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


Date.now() и арифметика времени

Самый низкоуровневый инструмент — вычислить разницу вручную:

function relativeTime(date, locale = 'ru') {
  const diff    = Date.now() - new Date(date).getTime();
  const seconds = Math.abs(diff) / 1000;
  const minutes = seconds / 60;
  const hours   = minutes / 60;
  const days    = hours   / 24;
  const years   = days    / 365;

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

  if (seconds < 45)  return rtf.format(-Math.round(seconds), 'second');
  if (minutes < 45)  return rtf.format(-Math.round(minutes), 'minute');
  if (hours   < 22)  return rtf.format(-Math.round(hours),   'hour');
  if (days    < 26)  return rtf.format(-Math.round(days),    'day');
  if (days    < 320) return rtf.format(-Math.round(days / 30), 'month');
  return rtf.format(-Math.round(years), 'year');
}

Intl.RelativeTimeFormat

Появился в ES2020. Встроен во все современные браузеры:

const rtf = new Intl.RelativeTimeFormat('ru', {
  numeric: 'auto',  // 'auto' → "вчера"/"сегодня", 'always' → "1 день назад"
  style:   'long',  // 'long'|'short'|'narrow'
});

rtf.format(-1, 'day');      // "вчера"
rtf.format(-2, 'day');      // "2 дня назад"
rtf.format(-1, 'hour');     // "час назад"
rtf.format(2,  'minute');   // "через 2 минуты"
rtf.format(-1, 'year');     // "в прошлом году"

Поддержка браузеров Intl.RelativeTimeFormat

Браузер Поддержка
Chrome 71+
Firefox 65+
Safari 14+
Edge 79+
Node.js 12+
IE 11
Safari 13

Функция forHumans без зависимостей

function forHumans(date, locale = 'ru') {
  const now      = Date.now();
  const then     = new Date(date).getTime();
  const diffMs   = then - now;
  const diffSec  = diffMs / 1000;
  const absSec   = Math.abs(diffSec);

  const units = [
    { limit: 45,      unit: 'second', value: absSec           },
    { limit: 45 * 60, unit: 'minute', value: absSec / 60      },
    { limit: 22 * 3600, unit: 'hour', value: absSec / 3600    },
    { limit: 26 * 86400, unit: 'day', value: absSec / 86400   },
    { limit: 320 * 86400, unit: 'month', value: absSec / (30 * 86400)  },
    { limit: Infinity,    unit: 'year',  value: absSec / (365 * 86400) },
  ];

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

  for (const { limit, unit, value } of units) {
    if (absSec < limit) {
      const rounded = Math.round(value) * (diffSec < 0 ? -1 : 1);
      return rtf.format(rounded, unit);
    }
  }
}

Разница между Intl.RTF и timeago.js

Критерий Intl.RelativeTimeFormat timeago.js
Зависимости Нет — встроен в браузер +2.5KB gzip
Поддержка браузеров Chrome 71, Safari 14 Широкая (даже IE с полифилом)
Кастомные локали Нет — только системные register() для любого языка
Автообновление DOM Нет render() + cancel()
Точность форм Высокая — ICU данные Зависит от качества локали
Конфигурация numeric, style LocaleFunc — полный контроль

Полифилл для Safari 13 и IE11

npm install @formatjs/intl-relativetimeformat
import '@formatjs/intl-relativetimeformat/polyfill';
import '@formatjs/intl-relativetimeformat/locale-data/ru';
import '@formatjs/intl-relativetimeformat/locale-data/en';

Date.prototype методы

const date = new Date('2025-06-01T12:00:00Z');

date.getTime();              // 1748779200000 — timestamp мс
date.toISOString();         // "2025-06-01T12:00:00.000Z"
date.toLocaleDateString('ru-RU'); // "1.06.2025"
date.toLocaleTimeString('ru-RU'); // "15:00:00"
date.toLocaleString('ru-RU');     // "1.06.2025, 15:00:00"

Эти методы не дают “2 дня назад” — только абсолютные значения.


Комбинирование: Intl.RTF + кеш

const rtfCache = new Map();

function getCachedRTF(locale) {
  if (!rtfCache.has(locale)) {
    rtfCache.set(locale, new Intl.RelativeTimeFormat(locale, { numeric: 'auto' }));
  }
  return rtfCache.get(locale);
}

// Создание Intl.RelativeTimeFormat дорогостоящее — кешировать
function relativeTime(date, locale = 'ru') {
  const rtf = getCachedRTF(locale);
  const diff = (new Date(date) - Date.now()) / 1000;
  // ... выбор unit и вызов rtf.format
}

Когда использовать нативный API вместо timeago.js

  • Проект не поддерживает IE11 и Safari < 14.
  • Нужна только базовая функциональность без автообновления.
  • Размер бандла критичен.
  • Нет нестандартных локалей — только системные браузерные.
  • Нет потребности в render/cancel для автоматических DOM-обновлений.