Intl.RelativeTimeFormat — нативный браузерный API для
форматирования относительного времени. В ряде случаев он полностью
заменяет timeago.js без дополнительных зависимостей.
const rtf = new Intl.RelativeTimeFormat('ru', {
numeric: 'auto', // 'auto' или 'always'
style: 'long', // 'long', 'short', 'narrow'
});
// Прошлое
rtf.format(-30, 'second'); // "30 секунд назад"
rtf.format(-5, 'minute'); // "5 минут назад"
rtf.format(-2, 'hour'); // "2 часа назад"
rtf.format(-1, 'day'); // "вчера"
rtf.format(-3, 'day'); // "3 дня назад"
rtf.format(-2, 'week'); // "2 недели назад"
rtf.format(-1, 'month'); // "в прошлом месяце"
rtf.format(-1, 'year'); // "в прошлом году"
// Будущее
rtf.format(5, 'minute'); // "через 5 минут"
rtf.format(1, 'day'); // "завтра"
rtf.format(1, 'week'); // "на следующей неделе"
const auto = new Intl.RelativeTimeFormat('ru', { numeric: 'auto' });
const always = new Intl.RelativeTimeFormat('ru', { numeric: 'always' });
auto.format(-1, 'day'); // "вчера"
always.format(-1, 'day'); // "1 день назад"
auto.format(0, 'day'); // "сегодня"
always.format(0, 'day'); // "через 0 дней"
const ru = (style) => new Intl.RelativeTimeFormat('ru', { numeric: 'auto', style });
ru('long').format(-5, 'minute'); // "5 минут назад"
ru('short').format(-5, 'minute'); // "5 мин. назад"
ru('narrow').format(-5, 'minute'); // "5 мин. назад" (иногда идентично short)
function relativeFormat(date, locale = 'ru') {
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
const diffMs = new Date(date).getTime() - Date.now();
const absSec = Math.abs(diffMs / 1000);
const sign = diffMs < 0 ? -1 : 1;
if (absSec < 45) return rtf.format(sign * Math.round(absSec), 'second');
if (absSec < 45 * 60) return rtf.format(sign * Math.round(absSec / 60), 'minute');
if (absSec < 22 * 3600) return rtf.format(sign * Math.round(absSec / 3600), 'hour');
if (absSec < 26 * 86400) return rtf.format(sign * Math.round(absSec / 86400),'day');
if (absSec < 320 * 86400) return rtf.format(sign * Math.round(absSec / (30 * 86400)), 'month');
return rtf.format(sign * Math.round(absSec / (365 * 86400)), 'year');
}
Создание Intl.RelativeTimeFormat — относительно дорогая
операция. При частом вызове нужно кешировать:
const rtfCache = new Map();
function getRTF(locale, options = {}) {
const key = `${locale}:${JSON.stringify(options)}`;
if (!rtfCache.has(key)) {
rtfCache.set(key, new Intl.RelativeTimeFormat(locale, options));
}
return rtfCache.get(key);
}
Intl.RelativeTimeFormat не предоставляет
render/cancel. Реализовать самостоятельно:
function startRelativeTimeUpdates(selector = '[datetime]', locale = 'ru') {
const elements = document.querySelectorAll(selector);
function update() {
elements.forEach(el => {
const datetime = el.getAttribute('datetime');
if (!datetime) return;
const next = relativeFormat(datetime, locale);
if (el.textContent !== next) el.textContent = next;
});
}
update(); // Сразу
const id = setInterval(update, 30_000);
return () => clearInterval(id); // cleanup
}
import { format } from 'timeago.js';
const date = new Date(Date.now() - 2 * 3600_000); // 2 часа назад
// timeago.js
format(date, 'ru'); // "2 часа назад"
// Intl.RelativeTimeFormat
relativeFormat(date, 'ru'); // "2 часа назад"
Для стандартных случаев вывод идентичен. Различия:
const rtf = new Intl.RelativeTimeFormat('ru', { numeric: 'auto' });
rtf.formatToParts(-5, 'minute');
// [
// { type: 'integer', value: '5' },
// { type: 'literal', value: ' ' },
// { type: 'literal', value: 'минут' },
// { type: 'literal', value: ' назад' },
// ]
formatToParts позволяет обернуть число в
<span> или применить стили:
function formatWithHighlight(value, unit, locale = 'ru') {
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'always' });
const parts = rtf.formatToParts(value, unit);
return parts.map(part =>
part.type === 'integer'
? `<strong>${part.value}</strong>`
: part.value
).join('');
}
render для автоматического обновления DOM
через встроенные таймеры.LocaleFunc.