Утилитарные функции

Стандартное 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());
}

Форматирование с tooltip

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);
}

Утилита freshness

Категоризация “свежести” контента:

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 часа назад"

Утилита diffHuman

Вычисление разницы с выводом в нескольких единицах:

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}мин`,
  };
}