Длительность интервала: intervalToDuration

В библиотеке date-fns функция intervalToDuration предназначена для преобразования временного интервала между двумя датами в структурированное представление длительности. В отличие от простого вычисления разницы в миллисекундах или днях, результат возвращается в виде объекта с разложением по календарным единицам: годам, месяцам, дням, часам, минутам и секундам.


Сигнатура функции

intervalToDuration({ start, end })

Параметры

  • start — начальная дата интервала (Date | number)
  • end — конечная дата интервала (Date | number)

Оба значения могут быть объектами Date или числом (timestamp в миллисекундах).


Структура возвращаемого результата

Функция возвращает объект следующего вида:

{
  years: number,
  months: number,
  days: number,
  hours: number,
  minutes: number,
  seconds: number
}

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


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

intervalToDuration работает не как арифметическая разница в миллисекундах, а как календарное разложение интервала. Это означает:

  • учитываются длины месяцев (28–31 день)
  • учитываются високосные годы
  • вычисления выполняются последовательно от крупных единиц к мелким
  • каждая единица «вычитается» из интервала перед вычислением следующей

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


Пример базового использования

import { intervalToDuration } from 'date-fns'

const result = intervalToDuration({
  start: new Date(2023, 0, 1),
  end: new Date(2025, 5, 10)
})

console.log(result)

Возможный результат:

{
  years: 2,
  months: 5,
  days: 9,
  hours: 0,
  minutes: 0,
  seconds: 0
}

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

Интервал в пределах одного дня

intervalToDuration({
  start: new Date(2024, 0, 1, 10, 15, 0),
  end: new Date(2024, 0, 1, 12, 45, 30)
})

Результат:

{
  years: 0,
  months: 0,
  days: 0,
  hours: 2,
  minutes: 30,
  seconds: 30
}

Интервал через несколько месяцев

intervalToDuration({
  start: new Date(2024, 0, 15),
  end: new Date(2024, 3, 10)
})

Результат будет учитывать разную длину месяцев:

{
  years: 0,
  months: 2,
  days: 26,
  hours: 0,
  minutes: 0,
  seconds: 0
}

Отличие от differenceIn*

В экосистеме date-fns существует набор функций differenceInDays, differenceInMonths, differenceInSeconds и т.д.

Ключевое различие:

  • differenceIn* возвращает разницу в одной единице измерения
  • intervalToDuration раскладывает интервал сразу по всем единицам

Пример:

differenceInDays(end, start) // одно число

intervalToDuration({ start, end }) // объект с разложением

Особенности расчёта календарных единиц

Месяцы различной длины

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

Високосные годы

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

Последовательное «вычитание» единиц

Алгоритм работает по принципу жадного разложения:

  1. Сначала вычисляются годы
  2. Затем месяцы
  3. Потом дни
  4. Далее часы, минуты и секунды

Каждый шаг уменьшает остаток интервала.


Нормализация входных данных

Функция автоматически:

  • преобразует number в Date
  • сравнивает даты и при необходимости меняет порядок (если start > end)
  • работает с отрицательными интервалами как с перевёрнутыми

Пример:

intervalToDuration({
  start: new Date(2025, 1, 1),
  end: new Date(2024, 1, 1)
})

Результат будет эквивалентен нормализованному прямому интервалу.


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

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

const d = intervalToDuration({ start, end })

const formatted =
  `${d.years}г ${d.months}м ${d.days}д ` +
  `${d.hours}ч ${d.minutes}м ${d.seconds}с`

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


Ограничения и особенности поведения

  • не возвращает миллисекунды
  • зависит от календаря, а не фиксированных интервалов времени
  • результат может отличаться от простых арифметических вычислений
  • не предназначена для высокоточных временных измерений (например, в логировании событий с миллисекундной точностью)

Типичные сценарии применения

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

Поведение при нулевых значениях

Если разница между датами отсутствует:

intervalToDuration({
  start: new Date(2024, 0, 1),
  end: new Date(2024, 0, 1)
})

Результат:

{
  years: 0,
  months: 0,
  days: 0,
  hours: 0,
  minutes: 0,
  seconds: 0
}

Интерпретация результата в прикладной логике

Объект длительности не содержит информации о «плоском» времени. Он отражает структуру календарного интервала, поэтому:

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

Такой формат предназначен исключительно для представления, а не для точных вычислений.