Расчет длительности периодов

Расчёт длительности периодов в Day.js строится вокруг операции вычисления разницы между двумя моментами времени. Основной инструмент — метод diff(), который возвращает количество единиц времени между датами.

import dayjs from 'dayjs';

const start = dayjs('2026-01-01');
const end = dayjs('2026-01-10');

const days = end.diff(start, 'day');

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

  • millisecond
  • second
  • minute
  • hour
  • day
  • week
  • month
  • year

Ключевая особенность: diff() по умолчанию возвращает целое число, отбрасывая дробную часть.

end.diff(start, 'day'); // 9

Если требуется более точный результат, используется третий параметр — точность округления.

end.diff(start, 'day', true); // 9.0

Единицы измерения и влияние масштаба времени

Выбор единицы измерения критически влияет на результат вычисления длительности.

const a = dayjs('2026-01-01T00:00:00');
const b = dayjs('2026-01-02T12:00:00');

b.diff(a, 'day');     // 1
b.diff(a, 'hour');    // 36
b.diff(a, 'minute');  // 2160

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


Проблема неполных месяцев и лет

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

const a = dayjs('2026-01-31');
const b = dayjs('2026-02-28');

b.diff(a, 'month'); // 0

Хотя календарно прошло почти 1 месяц, результат равен 0, поскольку Day.js считает только завершённые месячные циклы.

Аналогично с годами:

dayjs('2027-01-01').diff(dayjs('2026-12-31'), 'year'); // 0

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


Расширенные расчёты с точностью

Для более гибкого анализа используется дробная форма:

const diff = b.diff(a, 'day', true);

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


Работа с диапазонами через startOf и endOf

При расчёте длительности периодов часто требуется нормализация границ интервала.

const start = dayjs('2026-01-15').startOf('month');
const end = dayjs('2026-01-15').endOf('month');

const days = end.diff(start, 'day');

Методы:

  • startOf(unit) — приводит дату к началу периода
  • endOf(unit) — приводит дату к концу периода

Применение важно при расчётах календарных интервалов: месяц, квартал, неделя.


Итерация по периодам

Расчёт длительности часто сопровождается разбиением интервала на шаги.

let cursor = dayjs('2026-01-01');
const end = dayjs('2026-01-10');

while (cursor.isBefore(end)) {
  cursor = cursor.add(1, 'day');
}

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


Учет временных зон и UTC

При работе с длительностями критично учитывать влияние часовых поясов.

Для стабильных вычислений применяется плагин UTC:

import utc from 'dayjs/plugin/utc';
dayjs.extend(utc);

const a = dayjs.utc('2026-03-29T00:00:00Z');
const b = dayjs.utc('2026-03-30T00:00:00Z');

b.diff(a, 'day'); // 1

Без нормализации к UTC возможны смещения из-за переходов на летнее/зимнее время.


Влияние перехода на летнее время

DST (Daylight Saving Time) создаёт интервалы, которые не равны 24 часам.

const a = dayjs('2026-03-28T00:00:00');
const b = dayjs('2026-03-29T00:00:00');

b.diff(a, 'day'); // может быть 1, но фактически не 24 часа

При расчёте точной длительности в часах лучше использовать:

b.diff(a, 'hour');

Использование plugin duration для представления длительности

Для работы с длительностями как с отдельными объектами применяется plugin duration.

import duration from 'dayjs/plugin/duration';
dayjs.extend(duration);

const diffMs = end.diff(start);
const d = dayjs.duration(diffMs);

Объект duration позволяет:

  • форматировать длительность
  • извлекать компоненты времени
  • выполнять арифметику
d.asDays();    // общее число дней (с дробью)
d.asHours();   // часы
d.minutes();   // минуты компонента

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

Day.js не имеет встроенного универсального форматтера, поэтому используется комбинация методов:

const d = dayjs.duration(end.diff(start));

const result = {
  days: Math.floor(d.asDays()),
  hours: d.hours(),
  minutes: d.minutes()
};

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


Расчёт возрастных и долгосрочных периодов

Типичный сценарий — вычисление возраста или стажа.

const birth = dayjs('1995-06-10');
const now = dayjs();

const years = now.diff(birth, 'year');
const months = now.diff(birth.add(years, 'year'), 'month');

Комбинирование единиц позволяет получить более точную структуру периода.


Финансовые и подписочные интервалы

При расчёте подписок важна работа с календарными циклами.

const start = dayjs('2026-01-15');
const next = start.add(1, 'month');

const days = next.diff(start, 'day');

Здесь длительность может варьироваться (28–31 день), поэтому расчёт через месяцы предпочтительнее фиксированных дней.


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

При необходимости деления периода на равные части используется комбинация diff и add.

const start = dayjs('2026-01-01');
const end = dayjs('2026-02-01');

const totalDays = end.diff(start, 'day');
const step = totalDays / 4;

const segments = [];

for (let i = 0; i <= 4; i++) {
  segments.push(start.add(step * i, 'day'));
}

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


Проверка включительности границ интервала

При расчётах периодов часто возникает вопрос: включается ли конечная точка.

const diff = end.diff(start, 'day');
const inclusive = diff + 1;

Day.js по умолчанию использует полуоткрытый интервал: начало включается, конец исключается.


Практика точных вычислений для календарных систем

Для систем учёта времени корректная стратегия расчёта длительности строится на следующих принципах:

  • нормализация к UTC при критичных расчётах
  • использование diff() только для измерения, а не генерации интервалов
  • применение startOf() и endOf() для календарных границ
  • избегание month и year в аналитических расчётах без понимания их дискретности

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

При обработке больших массивов дат важно минимизировать создание объектов Day.js.

for (let i = 0; i < dates.length; i++) {
  const diff = dayjs(dates[i]).diff(base, 'day');
}

Более эффективный подход — предобработка базовых значений и повторное использование объектов, где это возможно.


Сложные цепочки расчёта длительности

Комбинированные сценарии часто требуют последовательных преобразований:

const start = dayjs('2026-01-01');
const checkpoint = start.add(10, 'day');
const end = start.add(2, 'month');

const firstPhase = checkpoint.diff(start, 'day');
const secondPhase = end.diff(checkpoint, 'day');

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