Вычитание времени

Операция вычитания времени в Moment.js основана на изменении текущего временного значения с помощью метода subtract, который позволяет уменьшать дату или время на заданный интервал. Поддерживаются различные единицы измерения, включая дни, месяцы, годы, часы, минуты, секунды и миллисекунды. Работа метода опирается на внутреннюю мутацию объекта Moment, что критично для понимания поведения цепочек вызовов.

Базовая форма метода:

moment().subtract(Number, String);
moment().subtract(Number, String, Boolean);

Первый аргумент задаёт величину вычитания, второй — единицу измерения.

Примеры использования:

moment().subtract(1, 'days');
moment().subtract(2, 'months');
moment().subtract(10, 'minutes');
moment().subtract(500, 'milliseconds');

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

  • years
  • quarters
  • months
  • weeks
  • days
  • hours
  • minutes
  • seconds
  • milliseconds

В некоторых версиях Moment.js допускаются сокращённые формы:

moment().subtract(1, 'y');
moment().subtract(1, 'M');
moment().subtract(1, 'd');
moment().subtract(1, 'h');
moment().subtract(1, 'm');
moment().subtract(1, 's');

Принцип мутации объекта Moment

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

const m = moment('2026-01-10');

m.subtract(1, 'days');

console.log(m.format('YYYY-MM-DD')); // 2026-01-09

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

const m = moment('2026-01-10');

m.subtract(1, 'days');
m.subtract(2, 'days');

console.log(m.format('YYYY-MM-DD')); // 2026-01-07

Для предотвращения побочных эффектов используется клонирование:

const m1 = moment('2026-01-10');
const m2 = m1.clone().subtract(5, 'days');

console.log(m1.format('YYYY-MM-DD')); // 2026-01-10
console.log(m2.format('YYYY-MM-DD')); // 2026-01-05

Вычитание нескольких единиц времени

Метод поддерживает последовательное применение разных единиц:

moment('2026-01-10')
  .subtract(1, 'years')
  .subtract(2, 'months')
  .subtract(10, 'days');

Эквивалентная форма с объектом:

moment('2026-01-10').subtract({
  years: 1,
  months: 2,
  days: 10
});

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

Особенности работы с календарными единицами

Некоторые единицы времени зависят от календаря:

Месяцы

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

moment('2026-03-31').subtract(1, 'month');

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

Годы (включая високосные)

moment('2024-02-29').subtract(1, 'year');

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

Часовые операции и переходы времени

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

moment('2026-01-10T00:30:00').subtract(2, 'hours');

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

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

Разница между subtract и diff

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

Пример subtract:

const a = moment('2026-01-10');
a.subtract(5, 'days');

Пример diff:

const a = moment('2026-01-10');
const b = moment('2026-01-05');

a.diff(b, 'days'); // 5

Ключевое различие заключается в том, что diff возвращает числовой результат, а subtract трансформирует объект.

Работа с отрицательными значениями

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

moment('2026-01-10').subtract(-5, 'days');

Эквивалентно:

moment('2026-01-10').add(5, 'days');

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

Вычитание и цепочки методов форматирования

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

moment()
  .subtract(7, 'days')
  .format('YYYY-MM-DD');

Также возможно комбинирование с локализацией:

moment()
  .subtract(1, 'month')
  .locale('ru')
  .format('LL');

Работа с несколькими операциями в одном выражении

Цепочки позволяют строить сложные временные вычисления:

moment('2026-12-31T23:59:59')
  .subtract(1, 'year')
  .subtract(2, 'months')
  .subtract(3, 'days')
  .subtract(4, 'hours')
  .format();

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

Влияние порядка операций

Порядок вычитания имеет значение, особенно при работе с календарными единицами:

moment('2026-03-31')
  .subtract(1, 'month')
  .subtract(10, 'days');

и

moment('2026-03-31')
  .subtract(10, 'days')
  .subtract(1, 'month');

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

Использование объекта конфигурации

Объектный вариант позволяет задавать отрицательные и положительные смещения одновременно:

moment('2026-01-10').subtract({
  days: 10,
  hours: 5,
  minutes: 30
});

При этом отсутствует необходимость множественных вызовов метода.

Вычитание и UTC-режим

При работе с UTC-датами поведение аналогично, но все операции выполняются в универсальном времени:

moment.utc('2026-01-10T00:00:00').subtract(1, 'days');

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

Особенности работы с миллисекундами

Мелкие единицы времени позволяют выполнять точные вычисления:

moment().subtract(1500, 'milliseconds');

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

Сравнение с нативным Date

Встроенный Date не предоставляет удобного метода вычитания интервалов, что требует ручного расчёта:

const d = new Date();
d.setDate(d.getDate() - 5);

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

Пограничные случаи и корректировки

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

  • переход через границы месяцев с разной длиной
  • корректировка дат 29, 30, 31 числа
  • влияние високосных годов
  • переходы через DST (летнее/зимнее время)

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