Форматирование относительного времени с formatDistance

Функция formatDistance из библиотеки date-fns предназначена для преобразования разницы между двумя датами в человекочитаемое выражение, отражающее относительную длительность: «около 5 минут», «2 месяца», «менее 10 секунд». Основная цель — представление временного интервала в естественной языковой форме без ручной обработки единиц времени.


Сигнатура и базовое поведение

Функция имеет следующий вид:

formatDistance(date, baseDate, [options])

Параметры:

  • date — дата, расстояние до которой вычисляется
  • baseDate — базовая дата для сравнения
  • options — объект конфигурации (опционально)

Возвращаемое значение — строка, описывающая расстояние между датами.


Принцип вычисления расстояния

formatDistance сначала определяет абсолютную разницу между датами, затем подбирает наиболее подходящую единицу измерения:

  • секунды
  • минуты
  • часы
  • дни
  • месяцы
  • годы

После этого результат округляется до наиболее «естественного» значения и преобразуется в текст.

Пример логики:

  • 45 секунд → «less than a minute»
  • 1.2 минуты → «1 minute»
  • 3 часа → «3 hours»
  • 15 дней → «about 2 weeks»

Базовое использование

import { formatDistance } from 'date-fns';

const result = formatDistance(
  new Date(2024, 0, 1),
  new Date(2024, 0, 5)
);

console.log(result);

Результат:

"4 days"

Функция всегда возвращает относительное расстояние, а не абсолютную разницу в конкретной единице.


Работа с направлением времени

Порядок аргументов влияет на смысл результата. Разница всегда считается как расстояние от baseDate к date.

formatDistance(
  new Date(2024, 0, 10),
  new Date(2024, 0, 1)
);

Результат:

"9 days"

Если даты поменять местами, текст останется тем же, но смысл в сочетании с суффиксом может измениться при использовании опций.


Добавление суффикса времени

Опция addSuffix добавляет контекст относительности: «ago» или «in».

import { formatDistance } from 'date-fns';

const past = formatDistance(
  new Date(2024, 0, 1),
  new Date(2024, 0, 10),
  { addSuffix: true }
);

console.log(past);

Результат:

"9 days ago"

А для будущего времени:

formatDistance(
  new Date(2024, 0, 10),
  new Date(2024, 0, 1),
  { addSuffix: true }
);
"in 9 days"

Работа с малыми интервалами

Для очень малых разниц используется приближение:

formatDistance(
  new Date(2024, 0, 1, 12, 0, 30),
  new Date(2024, 0, 1, 12, 0, 0)
);

Результат:

"less than a minute"

При увеличении интервала до минут и секунд функция начинает округлять значения:

  • 1–44 секунды → less than a minute
  • 45–89 секунд → about a minute

Локализация и переводы

date-fns поддерживает локализацию через объект locale.

import { formatDistance } from 'date-fns';
import { ru } from 'date-fns/locale';

formatDistance(
  new Date(2024, 0, 10),
  new Date(2024, 0, 1),
  { locale: ru }
);

Результат:

"9 дней"

Локализация влияет не только на перевод слов, но и на грамматические формы числительных, что особенно важно для языков с падежной системой.


Особенности склонений и языковых правил

В локализованных версиях учитываются:

  • род существительных
  • падежи
  • правила числительных
  • формы множественного числа

Например:

  • «1 день»
  • «2 дня»
  • «5 дней»

Эти различия полностью контролируются внутренними правилами локали, без необходимости ручной обработки.


Отличие от formatDistanceStrict

В библиотеке существует альтернативная функция:

  • formatDistance
  • formatDistanceStrict

formatDistance

Использует приближённые выражения:

  • about 1 hour
  • almost 2 years
  • over 3 months

formatDistanceStrict

Использует точные значения:

import { formatDistanceStrict } from 'date-fns';

formatDistanceStrict(
  new Date(2024, 0, 1),
  new Date(2024, 0, 2)
);

Результат:

"1 day"

Различие заключается в стиле:

  • formatDistance — разговорная форма
  • formatDistanceStrict — точная формулировка без «about», «over», «less than»

Типичные категории округления

Функция использует систему приближений:

Интервал Результат
< 30 сек less than a minute
30–90 сек 1 minute
90 сек – 45 мин X minutes
45–90 мин about 1 hour
90 мин – 22 ч X hours
22–36 ч 1 day
36 ч – 25 д X days
25–45 д about 1 month
45–345 д X months
345–547 д about 1 year

Эта система обеспечивает читаемость, но не точность.


Обработка ошибок и нестандартных значений

Если переданы некорректные даты:

  • Invalid Date
  • undefined
  • null

поведение может привести к выбросу исключения или возвращению некорректного результата в зависимости от версии библиотеки.

Рекомендуется предварительная валидация через:

  • isValid
  • parseISO

Интеграция с форматированием интерфейсов

formatDistance часто применяется в интерфейсах, где важно отображение динамического времени:

  • уведомления («5 минут назад»)
  • комментарии («2 часа назад»)
  • события («через 3 дня»)
  • ленты активности

При этом функция используется совместно с addSuffix: true, формируя законченные фразы без дополнительной обработки.


Использование в реальном времени

При динамическом обновлении интерфейса значение может пересчитываться периодически:

setInterval(() => {
  console.log(
    formatDistance(new Date(), pastDate, { addSuffix: true })
  );
}, 60000);

Такая схема позволяет поддерживать актуальность отображаемого времени без ручных пересчётов единиц.


Поведение при больших интервалах

При работе с многолетними интервалами функция переходит к годам и месяцам:

formatDistance(
  new Date(2010, 0, 1),
  new Date(2024, 0, 1)
);

Результат:

"about 14 years"

Использование слова «about» отражает внутреннее округление и приближенность расчёта.


Комбинация с другими функциями date-fns

В практических сценариях formatDistance часто используется вместе с:

  • parseISO — парсинг строк дат
  • differenceInDays — точные расчёты
  • format — форматирование абсолютных дат
  • subDays, addDays — арифметика дат

Пример комбинирования:

import { formatDistance, parseISO } from 'date-fns';

const date = parseISO('2024-01-10T12:00:00Z');

formatDistance(date, new Date(), { addSuffix: true });

Производственные особенности

При массовом использовании следует учитывать:

  • функция не кеширует результаты
  • каждый вызов выполняет полный пересчёт разницы
  • локализация увеличивает стоимость вычисления
  • при больших списках событий возможна нагрузка при частом рендере

Оптимизация обычно достигается через:

  • мемоизацию
  • предрасчёт значений
  • ограничение частоты обновления UI

Поведение при границах единиц времени

Особое внимание уделяется переходным состояниям:

  • 59 секунд → less than a minute
  • 60 секунд → 1 minute
  • 89 секунд → 1 minute
  • 90 секунд → 2 minutes

Такая модель сглаживает восприятие времени и уменьшает «скачки» значений.


Контроль формата через опции

Основные доступные опции:

  • addSuffix — добавление «ago / in»
  • locale — выбор языка и правил форматирования

Дополнительные настройки напрямую в formatDistance ограничены, поскольку библиотека ориентирована на стандартизированное поведение.


Поведение в контексте UTC и часовых поясов

Функция работает с объектами Date, поэтому:

  • учитывает локальное время исполнения среды
  • не требует явного указания временной зоны
  • зависит от интерпретации JavaScript Date API

При работе с UTC-строками рекомендуется предварительный парсинг через ISO-форматы.


Распространённые паттерны использования

  • «время с момента события»
formatDistance(eventDate, new Date(), { addSuffix: true });
  • «время до события»
formatDistance(new Date(), eventDate, { addSuffix: true });
  • «нейтральное расстояние»
formatDistance(date1, date2);

Поведение при одинаковых датах

Если даты совпадают:

formatDistance(new Date(), new Date());

Результат:

"less than a minute"

Это связано с минимальной гранулярностью измерения, которая не опускается до нуля в человекочитаемом виде.