Форматирование даты

Intl.DateTimeFormat и Intl.RelativeTimeFormat — нативное форматирование дат на русском без библиотек.

Задача

Нужно отображать даты в читаемом виде: «12 апреля 2026», «вчера», «3 часа назад», «Пн, 09.04.2026». Библиотеки типа date-fns или dayjs — избыточны для базовых задач.

Решение

Форматирование через Intl.DateTimeFormat:

const now = new Date();

// Полная дата на русском
const fullDate = new Intl.DateTimeFormat('ru-RU', {
  day: 'numeric',
  month: 'long',
  year: 'numeric',
}).format(now);
// '9 апреля 2026 г.'

// Короткая дата
const short = new Intl.DateTimeFormat('ru-RU', {
  day: '2-digit',
  month: '2-digit',
  year: 'numeric',
}).format(now);
// '09.04.2026'

// Дата + время
const dateTime = new Intl.DateTimeFormat('ru-RU', {
  day: 'numeric',
  month: 'short',
  year: 'numeric',
  hour: '2-digit',
  minute: '2-digit',
}).format(now);
// '9 апр. 2026 г., 14:35'

// День недели
const withDay = new Intl.DateTimeFormat('ru-RU', {
  weekday: 'short',
  day: 'numeric',
  month: 'short',
}).format(now);
// 'ср, 9 апр.'

Относительное время «N минут назад»:

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

function timeAgo(date) {
  const seconds = Math.round((date - Date.now()) / 1000);
  const minutes = Math.round(seconds / 60);
  const hours   = Math.round(minutes / 60);
  const days    = Math.round(hours / 24);
  const months  = Math.round(days / 30);
  const years   = Math.round(days / 365);

  if (Math.abs(seconds) < 60)   return rtf.format(seconds, 'second');
  if (Math.abs(minutes) < 60)   return rtf.format(minutes, 'minute');
  if (Math.abs(hours) < 24)     return rtf.format(hours, 'hour');
  if (Math.abs(days) < 30)      return rtf.format(days, 'day');
  if (Math.abs(months) < 12)    return rtf.format(months, 'month');
  return rtf.format(years, 'year');
}

timeAgo(new Date(Date.now() - 3 * 60 * 60 * 1000)); // '3 часа назад'
timeAgo(new Date(Date.now() - 24 * 60 * 60 * 1000)); // 'вчера'
timeAgo(new Date(Date.now() + 2 * 24 * 60 * 60 * 1000)); // 'послезавтра'

Парсинг ISO-строки из API:

// Правильный способ — ISO 8601 с часовым поясом
const date = new Date('2026-04-09T14:35:00Z');

// Опасно: 'YYYY-MM-DD' без времени парсится как UTC 00:00
// и может показать 'вчера' в часовых поясах UTC+X
const wrong = new Date('2026-04-09'); // может быть 8 апреля в UTC+0
const right  = new Date('2026-04-09T00:00:00'); // локальное время

Ключевые моменты

  • Intl.DateTimeFormat и Intl.RelativeTimeFormat — встроены в браузер, не нужен импорт.
  • Кешируй экземпляр Intl.DateTimeFormat при форматировании массива дат — создание дорого.
  • { numeric: 'auto' } в RelativeTimeFormat — «вчера»/«завтра» вместо «-1 день»/«1 день».
  • ISO 8601 с Z или +HH:mm — единственный безопасный формат для парсинга дат из API.

Варианты

  • date-fns — функциональный, tree-shakeable, самый популярный.
  • dayjs — Moment.js совместимый API, 2 KB.
  • Temporal API — будущий стандарт, пока нужен polyfill.

Связанные рецепты / темы