JavaScript предоставляет встроенные средства для форматирования относительного времени. Они не требуют дополнительных зависимостей, но отличаются по возможностям и поддержке браузеров.
Самый низкоуровневый инструмент — вычислить разницу вручную:
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');
}
Появился в 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'); // "в прошлом году"
| Браузер | Поддержка |
|---|---|
| Chrome 71+ | ✓ |
| Firefox 65+ | ✓ |
| Safari 14+ | ✓ |
| Edge 79+ | ✓ |
| Node.js 12+ | ✓ |
| IE 11 | ✗ |
| Safari 13 | ✗ |
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.RelativeTimeFormat | timeago.js |
|---|---|---|
| Зависимости | Нет — встроен в браузер | +2.5KB gzip |
| Поддержка браузеров | Chrome 71, Safari 14 | Широкая (даже IE с полифилом) |
| Кастомные локали | Нет — только системные | register() для любого языка |
| Автообновление DOM | Нет | render() + cancel() |
| Точность форм | Высокая — ICU данные | Зависит от качества локали |
| Конфигурация | numeric, style | LocaleFunc — полный контроль |
npm install @formatjs/intl-relativetimeformat
import '@formatjs/intl-relativetimeformat/polyfill';
import '@formatjs/intl-relativetimeformat/locale-data/ru';
import '@formatjs/intl-relativetimeformat/locale-data/en';
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 дня назад” — только абсолютные значения.
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
}
render/cancel для
автоматических DOM-обновлений.