Добавление и вычитание времени

Работа с датами почти всегда связана с изменением временных значений: прибавлением дней, вычислением будущих дат, вычитанием часов, переносом времени между временными зонами и расчётом интервалов. В библиотеке Luxon эти операции реализованы через методы plus() и minus().

Главная особенность Luxon заключается в том, что объекты DateTime являются неизменяемыми. Любое изменение создаёт новый объект, не затрагивая исходный.

import { DateTime } from "luxon";

const now = DateTime.now();

const tomorrow = now.plus({ days: 1 });

console.log(now.toISO());
console.log(tomorrow.toISO());

Исходный объект now остаётся прежним.


Метод plus()

Метод plus() добавляет к дате или времени указанные единицы.

Добавление дней

const date = DateTime.now();

const result = date.plus({ days: 5 });

console.log(result.toISO());

Объект внутри plus() представляет собой набор единиц времени.


Поддерживаемые единицы времени

Luxon поддерживает множество временных единиц:

Единица Ключ
Миллисекунды milliseconds
Секунды seconds
Минуты minutes
Часы hours
Дни days
Недели weeks
Месяцы months
Кварталы quarters
Годы years

Пример:

const result = DateTime.now().plus({
  years: 1,
  months: 2,
  days: 10,
  hours: 5
});

Добавление часов и минут

const meeting = DateTime.now().plus({
  hours: 2,
  minutes: 30
});

console.log(meeting.toFormat("HH:mm"));

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

Методы можно вызывать цепочкой.

const result = DateTime.now()
  .plus({ days: 1 })
  .plus({ hours: 3 })
  .plus({ minutes: 15 });

console.log(result.toISO());

Однако чаще удобнее передавать всё одним объектом.

const result = DateTime.now().plus({
  days: 1,
  hours: 3,
  minutes: 15
});

Метод minus()

Метод minus() работает аналогично, но выполняет вычитание.

Вычитание дней

const yesterday = DateTime.now().minus({ days: 1 });

console.log(yesterday.toISODate());

Вычитание часов

const past = DateTime.now().minus({
  hours: 6
});

console.log(past.toISO());

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

const result = DateTime.now().minus({
  years: 1,
  months: 2,
  weeks: 1,
  days: 3
});

Добавление месяцев

При работе с месяцами Luxon автоматически корректирует дату.

const date = DateTime.fromISO("2025-01-31");

const result = date.plus({ months: 1 });

console.log(result.toISODate());

Результат:

2025-02-28

Так происходит потому, что в феврале нет 31 числа.


Добавление лет

const date = DateTime.fromISO("2024-02-29");

const result = date.plus({ years: 1 });

console.log(result.toISODate());

Результат:

2025-02-28

Luxon корректно учитывает високосные годы.


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

Методы plus() и minus() фактически могут выполнять обе операции.

const result = DateTime.now().plus({ days: -5 });

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

const result = DateTime.now().minus({ days: 5 });

Но для читаемости лучше использовать соответствующий метод.


Добавление времени к фиксированной дате

const date = DateTime.fromObject({
  year: 2025,
  month: 5,
  day: 10,
  hour: 12
});

const upd ated = date.plus({
  days: 3,
  hours: 4
});

console.log(updated.toString());

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

Операции учитывают временную зону объекта.

const dt = DateTime.now().setZone("Europe/Berlin");

const result = dt.plus({ hours: 5 });

console.log(result.toString());

Если происходит переход между часовыми поясами или летним временем, Luxon корректно пересчитывает итоговое значение.


Переход на летнее время

Добавление часов

const dt = DateTime.fromISO(
  "2025-03-30T01:30",
  { zone: "Europe/Berlin" }
);

const result = dt.plus({ hours: 1 });

console.log(result.toString());

Во время перехода на летнее время некоторые часы могут отсутствовать. Luxon учитывает это автоматически.


Добавление календарных и временных единиц

Между добавлением часов и дней существует важная разница.

Добавление суток

const dt = DateTime.fromISO(
  "2025-03-30T00:00",
  { zone: "Europe/Berlin" }
);

const result = dt.plus({ days: 1 });

Добавление 24 часов

const result = dt.plus({ hours: 24 });

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

  • days изменяет календарную дату
  • hours добавляет точное количество часов

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

Для хранения промежутков времени применяется объект Duration.

import { Duration } from "luxon";

const duration = Duration.fromObject({
  days: 2,
  hours: 5
});

const result = DateTime.now().plus(duration);

console.log(result.toISO());

Создание Duration

const duration = Duration.fromObject({
  hours: 10,
  minutes: 45
});

Добавление Duration

const start = DateTime.now();

const duration = Duration.fromObject({
  weeks: 1,
  days: 2
});

const end = start.plus(duration);

console.log(end.toISO());

Вычитание Duration

const duration = Duration.fromObject({
  months: 3
});

const result = DateTime.now().minus(duration);

Нормализация Duration

Иногда длительность содержит значения, выходящие за пределы диапазона.

const duration = Duration.fromObject({
  minutes: 120
});

console.log(duration.toObject());

Нормализация:

const normalized = duration.normalize();

console.log(normalized.toObject());

Результат:

{
  hours: 2
}

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

Luxon поддерживает краткие формы единиц.

const result = DateTime.now().plus({
  d: 1,
  h: 5
});

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

{
  days: 1,
  hours: 5
}

Так код проще поддерживать.


Сравнение дат после изменения

const now = DateTime.now();

const future = now.plus({ days: 7 });

console.log(future > now);

Лучше использовать числовое значение:

console.log(future.toMillis() > now.toMillis());

Вычисление возраста даты

const createdAt = DateTime.fromISO("2025-01-10");

const daysPassed = DateTime.now()
  .diff(createdAt, "days")
  .days;

console.log(daysPassed);

Прибавление времени в цикле

let current = DateTime.now();

for (let i = 0; i < 5; i++) {
  current = current.plus({ days: 1 });

  console.log(current.toISODate());
}

Генерация последовательности дат

const start = DateTime.fromISO("2025-05-01");

const dates = [];

for (let i = 0; i < 7; i++) {
  dates.push(
    start.plus({ days: i }).toISODate()
  );
}

console.log(dates);

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

Добавление и вычитание часто используется вместе с Interval.

import { Interval } from "luxon";

const start = DateTime.now();

const end = start.plus({ hours: 2 });

const interval = Interval.fromDateTimes(start, end);

console.log(interval.length("minutes"));

Смещение времени назад и вперёд

const now = DateTime.now();

const previousHour = now.minus({ hours: 1 });

const nextHour = now.plus({ hours: 1 });

Изменение времени без изменения даты

const date = DateTime.now();

const updated = date.plus({
  hours: 3
});

Изменение даты без изменения времени

const updated = DateTime.now().plus({
  days: 10
});

Комбинирование plus() и startOf()

const nextMonth = DateTime.now()
  .plus({ months: 1 })
  .startOf("month");

console.log(nextMonth.toISO());

Комбинирование minus() и endOf()

const previousMonthEnd = DateTime.now()
  .minus({ months: 1 })
  .endOf("month");

console.log(previousMonthEnd.toISO());

Добавление времени к UTC-дате

const utc = DateTime.utc();

const result = utc.plus({
  hours: 12
});

console.log(result.toISO());

Практический пример: срок действия токена

const expiresAt = DateTime.now().plus({
  minutes: 30
});

console.log(expiresAt.toISO());

Проверка истечения:

const isExpired =
  DateTime.now() > expiresAt;

Практический пример: расписание публикаций

const publication = DateTime.now()
  .plus({ days: 7 })
  .se t({
    hour: 9,
    minute: 0
  });

console.log(publication.toString());

Практический пример: дедлайн проекта

const start = DateTime.fromISO("2025-05-01");

const deadline = start.plus({
  months: 2,
  weeks: 1
});

console.log(deadline.toISODate());

Типичные ошибки

Изменение исходного объекта

Неверное ожидание:

const now = DateTime.now();

now.plus({ days: 1 });

console.log(now.toISO());

now не изменится.

Правильно:

const updated = now.plus({ days: 1 });

Путаница между днями и часами

.plus({ days: 1 })

и

.plus({ hours: 24 })

не всегда эквивалентны.


Неправильная работа с часовыми поясами

DateTime.now().plus({ hours: 2 });

Если зона важна, её необходимо задавать явно:

DateTime.now()
  .setZone("Asia/Tokyo")
  .plus({ hours: 2 });

Производительность

Luxon создаёт новый объект при каждой операции.

const updated = date
  .plus({ days: 1 })
  .plus({ hours: 2 })
  .plus({ minutes: 30 });

При большом количестве вычислений лучше объединять изменения:

const updated = date.plus({
  days: 1,
  hours: 2,
  minutes: 30
});