Стандартное API timeago.js минималистично. Для реальных проектов полезно создать набор утилитарных функций, расширяющих базовые возможности библиотеки.
Защита от null, undefined и невалидных
дат:
import { format } from 'timeago.js';
export function safeTimeAgo(date, locale = 'ru', fallback = '—') {
if (date == null) return fallback;
const d = new Date(date);
if (isNaN(d.getTime())) return fallback;
return format(d, locale);
}
import { format } from 'timeago.js';
export function richTimeAgo(date, locale = 'ru') {
const d = new Date(date);
const diff = Date.now() - d.getTime();
return {
relative: format(d, locale),
absolute: d.toISOString(),
isPast: diff >= 0,
ageMs: Math.abs(diff),
ageDays: Math.abs(diff) / 86400000,
};
}
import { format } from 'timeago.js';
export function smartTimeAgo(date, {
locale = 'ru',
maxDays = 30,
dateFormat = undefined,
} = {}) {
const d = new Date(date);
const ageDays = Math.abs(Date.now() - d.getTime()) / 86400000;
if (ageDays > maxDays) {
return new Intl.DateTimeFormat(locale, dateFormat || {
day: 'numeric', month: 'long', year: 'numeric',
}).format(d);
}
return format(d, locale);
}
import { format } from 'timeago.js';
export function formatCollection(items, dateField, locale = 'ru') {
return items.map(item => ({
...item,
[dateField + 'Label']: safeTimeAgo(item[dateField], locale),
}));
}
// Использование
const enriched = formatCollection(posts, 'createdAt', 'ru');
// enriched[0].createdAtLabel → "5 минут назад"
export function detectLocale() {
const lang = navigator.language || navigator.userLanguage || 'en';
const map = {
'ru': 'ru', 'uk': 'uk', 'de': 'de', 'fr': 'fr',
'es': 'es', 'pt': 'pt_BR', 'zh': 'zh_CN', 'ja': 'ja',
'ko': 'ko', 'ar': 'ar',
};
const code = lang.split('-')[0];
return map[code] || 'en_US';
}
import { format } from 'timeago.js';
export function autoTimeAgo(date) {
return format(date, detectLocale());
}
import { format } from 'timeago.js';
export function timeAgoWithTooltip(element, date, locale = 'ru') {
const absolute = new Intl.DateTimeFormat(locale, {
dateStyle: 'medium',
timeStyle: 'short',
}).format(new Date(date));
element.setAttribute('title', absolute);
element.textContent = format(date, locale);
}
export function groupByPeriod(items, dateField) {
const now = Date.now();
const HOUR = 3600000;
const DAY = 86400000;
const groups = {
today: [],
yesterday: [],
thisWeek: [],
older: [],
};
items.forEach(item => {
const d = new Date(item[dateField]);
const age = now - d.getTime();
if (age < DAY) groups.today.push(item);
else if (age < DAY * 2) groups.yesterday.push(item);
else if (age < DAY * 7) groups.thisWeek.push(item);
else groups.older.push(item);
});
return groups;
}
import { format } from 'timeago.js';
export function startPollingUpdate(selector = '[data-timeago]', locale = 'ru', intervalMs = 30000) {
function update() {
document.querySelectorAll(selector).forEach(el => {
const date = el.getAttribute('datetime') || el.dataset.date;
if (date) el.textContent = format(date, locale);
});
}
update();
const id = setInterval(update, intervalMs);
return () => clearInterval(id);
}
Категоризация “свежести” контента:
export function freshness(date) {
const ms = Date.now() - new Date(date).getTime();
if (ms < 0) return { level: 'future', label: 'В будущем' };
if (ms < 60000) return { level: 'hot', label: 'Только что' };
if (ms < 3600000) return { level: 'fresh', label: 'Свежее' };
if (ms < 86400000) return { level: 'recent', label: 'Сегодня' };
if (ms < 7 * 86400000) return { level: 'old', label: 'На этой неделе' };
return { level: 'stale', label: 'Давно' };
}
Форматирование с фиксированным “сейчас”:
import { format } from 'timeago.js';
export function testFormat(date, relativeDate, locale = 'ru') {
return format(date, locale, { relativeDate: new Date(relativeDate) });
}
Использование в тестах:
const result = testFormat('2025-05-26T10:00:00Z', '2025-05-26T12:00:00Z', 'ru');
// → "2 часа назад"
Вычисление разницы с выводом в нескольких единицах:
export function diffHuman(from, to = new Date()) {
const ms = Math.abs(new Date(to).getTime() - new Date(from).getTime());
const seconds = Math.floor(ms / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
return {
ms, seconds, minutes, hours, days,
toString: () => `${days}д ${hours % 24}ч ${minutes % 60}мин`,
};
}