Математические операции с длительностями

Moment.js предоставляет слой абстракции над временем, в котором длительности становятся самостоятельными объектами, а не просто числовыми разницами в миллисекундах. Основная модель работы с длительностями строится вокруг moment.duration, позволяющей выполнять арифметику времени в разных единицах — от миллисекунд до лет — с автоматической нормализацией и конвертацией.

Длительность в Moment.js — это не просто число, а структура, хранящая компоненты времени:

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

Такое разделение важно, потому что разные единицы времени имеют неоднородную природу: дни и месяцы не эквивалентны в фиксированном числовом выражении.

const d = moment.duration(2, 'hours');

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

Базовые операции сложения длительностей

Сложение длительностей позволяет формировать составные интервалы времени.

const d1 = moment.duration(2, 'hours');
const d2 = moment.duration(30, 'minutes');

const result = moment.duration(d1 + d2);

Однако прямое сложение объектов через оператор + приводит к неявному преобразованию в число (миллисекунды), поэтому корректнее использовать явную работу с миллисекундами:

const result = moment.duration(
  d1.asMilliseconds() + d2.asMilliseconds()
);

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

Вычитание длительностей

Вычитание реализуется аналогично сложению через числовое представление:

const d1 = moment.duration(5, 'hours');
const d2 = moment.duration(90, 'minutes');

const diff = moment.duration(
  d1.asMilliseconds() - d2.asMilliseconds()
);

Результатом может быть отрицательная длительность, что важно учитывать при дальнейших операциях.

Нормализация результатов арифметики

После выполнения операций длительность может содержать «переполненные» единицы измерения. Например, 90 минут не обязаны автоматически превращаться в 1 час 30 минут в разложенном виде при ручной работе с компонентами.

Moment.js поддерживает автоматическую нормализацию при конвертации:

const d = moment.duration(90, 'minutes');

d.hours();   // 1
d.minutes(); // 30

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

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

Основная математическая модель операций с длительностями сводится к единой шкале — миллисекундам:

const total = d1.asMilliseconds() + d2.asMilliseconds();
const result = moment.duration(total);

Это позволяет:

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

Умножение длительности на коэффициент

Прямой операции умножения в API нет, поэтому используется преобразование:

const d = moment.duration(1, 'hour');

const doubled = moment.duration(d.asMilliseconds() * 2);

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

Деление длительности

Деление используется для вычисления среднего интервала или разбиения периода:

const d = moment.duration(2, 'hours');

const half = moment.duration(d.asMilliseconds() / 2);

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

Сложение длительности с временными значениями

Длительности часто используются совместно с моментами времени:

const start = moment('2026-01-01 10:00');
const duration = moment.duration(90, 'minutes');

const end = start.clone().add(duration);

Метод add интерпретирует длительность как смещение по временной оси.

Вычитание длительности из момента

const end = moment('2026-01-01 12:00');
const duration = moment.duration(30, 'minutes');

const start = end.clone().subtract(duration);

Операция эквивалентна смещению назад по временной шкале.

Арифметика с компонентами длительности

Moment.js позволяет работать с частями длительности напрямую:

const d = moment.duration({
  hours: 1,
  minutes: 45
});

d.add(15, 'minutes');

Однако такая форма требует осторожности: при сложных комбинациях возможны эффекты накопления значений без немедленной полной нормализации.

Преобразование единиц при операциях

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

const d = moment.duration(3600, 'seconds');

d.asMinutes(); // 60
d.asHours();    // 1

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

  • либо всегда asMilliseconds()
  • либо всегда конкретную единицу (если контекст ограничен)

Отрицательные длительности

При вычитании может возникнуть отрицательное значение:

const d1 = moment.duration(1, 'hour');
const d2 = moment.duration(2, 'hours');

const result = moment.duration(d1.asMilliseconds() - d2.asMilliseconds());

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

result.asMilliseconds(); // отрицательное число

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

Сравнение длительностей через числовую форму

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

const d1 = moment.duration(2, 'hours');
const d2 = moment.duration(90, 'minutes');

if (d1.asMilliseconds() > d2.asMilliseconds()) {
  // d1 больше
}

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

Накопительные операции

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

const durations = [
  moment.duration(30, 'minutes'),
  moment.duration(45, 'minutes'),
  moment.duration(1, 'hour')
];

let total = moment.duration(0);

for (const d of durations) {
  total = moment.duration(total.asMilliseconds() + d.asMilliseconds());
}

Такой подход гарантирует корректную агрегацию независимо от единиц.

Работа с дробными значениями

При операциях деления возможны дробные миллисекунды:

const d = moment.duration(1, 'second');
const result = moment.duration(d.asMilliseconds() / 3);

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

Комбинирование разных единиц в одной операции

При создании длительности из смешанных единиц арифметика становится неявной:

const d = moment.duration({
  days: 1,
  hours: 5,
  minutes: 30
});

При преобразовании в миллисекунды происходит сведение всех компонентов к общей шкале:

d.asMilliseconds();

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

Ограничения математической модели длительностей

Арифметика длительностей имеет фундаментальные ограничения:

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

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

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

Многократные преобразования длительности между единицами и обратно приводят к накоплению погрешностей:

let d = moment.duration(1, 'hour');

for (let i = 0; i < 10; i++) {
  d = moment.duration(d.asMilliseconds() + 1000);
}

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

Использование длительностей в цепочках вычислений

Длительности часто применяются в цепочках операций:

const base = moment.duration(1, 'hour');

const result = moment.duration(
  base.asMilliseconds()
  + moment.duration(30, 'minutes').asMilliseconds()
  - moment.duration(15, 'minutes').asMilliseconds()
);

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