Арифметика времени

Библиотека Js-joda предоставляет неизменяемую модель работы с датой и временем. Любая арифметическая операция создаёт новый объект, не изменяя исходный экземпляр. Такой подход исключает побочные эффекты и делает вычисления предсказуемыми.

Основные операции времени строятся вокруг методов:

  • plus()
  • minus()
  • plusDays(), plusHours() и аналогичных
  • minusWeeks(), minusMonths() и аналогичных
  • until()
  • Period
  • Duration

Добавление временных значений

Прибавление дней

const { LocalDate } = require('@js-joda/core');

const date = LocalDate.parse('2025-03-10');

const result = date.plusDays(5);

console.log(result.toString());

Результат:

2025-03-15

Исходный объект остаётся неизменным:

console.log(date.toString());
2025-03-10

Прибавление недель

const result = date.plusWeeks(2);

console.log(result.toString());
2025-03-24

Прибавление месяцев

const date = LocalDate.parse('2025-01-15');

const result = date.plusMonths(3);

console.log(result.toString());
2025-04-15

Прибавление лет

const result = date.plusYears(1);

console.log(result.toString());
2026-01-15

Вычитание временных значений

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

const date = LocalDate.parse('2025-06-20');

const result = date.minusDays(10);

console.log(result.toString());
2025-06-10

Вычитание месяцев

const result = date.minusMonths(2);

console.log(result.toString());
2025-04-20

Вычитание лет

const result = date.minusYears(5);

console.log(result.toString());
2020-06-20

Арифметика с LocalDateTime

Класс LocalDateTime позволяет выполнять вычисления с датой и временем одновременно.

const { LocalDateTime } = require('@js-joda/core');

const dateTime = LocalDateTime.parse('2025-03-10T12:30:00');

const result = dateTime
    .plusHours(5)
    .plusMinutes(45);

console.log(result.toString());
2025-03-10T18:15

Работа с временем через LocalTime

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

const { LocalTime } = require('@js-joda/core');

const time = LocalTime.parse('10:15');

const result = time.plusHours(3);

console.log(result.toString());
13:15

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

const result = time.plusMinutes(50);

console.log(result.toString());
11:05

Переход через полночь

const time = LocalTime.parse('23:30');

const result = time.plusHours(2);

console.log(result.toString());
01:30

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


Универсальный метод plus()

Метод plus() принимает количество единиц и тип временной единицы.

const { LocalDate, ChronoUnit } = require('@js-joda/core');

const date = LocalDate.parse('2025-01-01');

const result = date.plus(10, ChronoUnit.DAYS);

console.log(result.toString());
2025-01-11

Использование разных единиц времени

date.plus(2, ChronoUnit.WEEKS);
date.plus(3, ChronoUnit.MONTHS);
date.plus(1, ChronoUnit.YEARS);

Универсальный метод minus()

const result = date.minus(15, ChronoUnit.DAYS);

console.log(result.toString());
2024-12-17

Класс Period

Period описывает период в календарных единицах:

  • годы
  • месяцы
  • дни

Создание периода

const { Period } = require('@js-joda/core');

const period = Period.of(1, 2, 10);

console.log(period.toString());
P1Y2M10D

Прибавление периода

const { LocalDate } = require('@js-joda/core');

const date = LocalDate.parse('2025-01-01');

const period = Period.ofMonths(3);

const result = date.plus(period);

console.log(result.toString());
2025-04-01

Вычитание периода

const period = Period.ofDays(15);

const result = date.minus(period);

console.log(result.toString());
2024-12-17

Класс Duration

Duration предназначен для точного времени:

  • часы
  • минуты
  • секунды
  • наносекунды

Создание Duration

const { Duration } = require('@js-joda/core');

const duration = Duration.ofHours(5);

console.log(duration.toString());
PT5H

Duration с минутами и секундами

const duration = Duration.ofMinutes(90);

console.log(duration.toString());
PT1H30M

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

const { LocalDateTime, Duration } = require('@js-joda/core');

const dateTime = LocalDateTime.parse('2025-03-10T08:00');

const duration = Duration.ofHours(6);

const result = dateTime.plus(duration);

console.log(result.toString());
2025-03-10T14:00

Разница между Period и Duration

Period

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

Period.ofDays(1)

Означает:

+1 календарный день

Duration

Работает с точным количеством времени.

Duration.ofHours(24)

Означает:

+24 часа

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


Вычисление разницы между датами

Метод until()

const start = LocalDate.parse('2025-01-01');
const end = LocalDate.parse('2025-03-01');

const days = start.until(end, ChronoUnit.DAYS);

console.log(days);
59

Разница в месяцах

const months = start.until(end, ChronoUnit.MONTHS);

console.log(months);
2

Разница между временем

const start = LocalTime.parse('08:00');
const end = LocalTime.parse('12:30');

const hours = start.until(end, ChronoUnit.HOURS);

console.log(hours);
4

Разница в минутах

const minutes = start.until(end, ChronoUnit.MINUTES);

console.log(minutes);
270

Цепочки арифметических операций

Js-joda поддерживает fluent-интерфейс.

const result = LocalDate.parse('2025-01-01')
    .plusYears(1)
    .minusMonths(2)
    .plusDays(10);

console.log(result.toString());
2025-11-11

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

Автоматическая корректировка даты

const date = LocalDate.parse('2025-01-31');

const result = date.plusMonths(1);

console.log(result.toString());
2025-02-28

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


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

const date = LocalDate.parse('2024-01-31');

const result = date.plusMonths(1);

console.log(result.toString());
2024-02-29

Отрицательные значения

Методы допускают отрицательные аргументы.

const date = LocalDate.parse('2025-01-10');

console.log(date.plusDays(-5).toString());
2025-01-05

Фактически это эквивалентно:

date.minusDays(5)

Работа с ChronoUnit

Перечисление ChronoUnit содержит основные единицы времени:

ChronoUnit.NANOS
ChronoUnit.MICROS
ChronoUnit.MILLIS
ChronoUnit.SECONDS
ChronoUnit.MINUTES
ChronoUnit.HOURS
ChronoUnit.DAYS
ChronoUnit.WEEKS
ChronoUnit.MONTHS
ChronoUnit.YEARS
ChronoUnit.DECADES
ChronoUnit.CENTURIES

Комбинирование Period и Duration

const { LocalDateTime, Period, Duration } = require('@js-joda/core');

const dateTime = LocalDateTime.parse('2025-01-01T10:00');

const result = dateTime
    .plus(Period.ofMonths(1))
    .plus(Duration.ofHours(5));

console.log(result.toString());
2025-02-01T15:00

Арифметика ZonedDateTime

ZonedDateTime учитывает часовой пояс.

const {
    ZonedDateTime,
    ZoneId
} = require('@js-joda/core');

require('@js-joda/timezone');

const dateTime = ZonedDateTime.now(
    ZoneId.of('Europe/Moscow')
);

const result = dateTime.plusHours(3);

console.log(result.toString());

Переходы летнего времени

При использовании ZonedDateTime библиотека корректно обрабатывает:

  • DST-переходы
  • смещения UTC
  • изменения временных зон
const result = dateTime.plusDays(1);

Результат может отличаться от прибавления Duration.ofHours(24).


Сравнение арифметических подходов

Календарная арифметика

date.plusMonths(1)

Используется для:

  • бизнес-логики
  • календарей
  • расписаний
  • отчётных периодов

Точная временная арифметика

dateTime.plus(Duration.ofHours(24))

Используется для:

  • таймеров
  • логирования
  • измерения интервалов
  • системного времени

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

Ошибка: ожидание изменения объекта

Неверно:

let date = LocalDate.now();

date.plusDays(1);

console.log(date);

Правильно:

date = date.plusDays(1);

Ошибка: смешивание Period и Duration

Неверно считать их взаимозаменяемыми:

Period.ofDays(1)
Duration.ofHours(24)

Это разные типы арифметики.


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

const {
    LocalDate,
    Period
} = require('@js-joda/core');

const createdAt = LocalDate.parse('2025-01-10');

const expiresAt = createdAt.plus(
    Period.ofMonths(6)
);

console.log(expiresAt.toString());
2025-07-10

Практический пример расписания

const {
    LocalDateTime
} = require('@js-joda/core');

const meeting = LocalDateTime.parse(
    '2025-03-10T09:00'
);

const reminder = meeting.minusMinutes(30);

console.log(reminder.toString());
2025-03-10T08:30

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

const {
    LocalDateTime,
    ChronoUnit
} = require('@js-joda/core');

const start = LocalDateTime.parse(
    '2025-03-10T08:00'
);

const end = LocalDateTime.parse(
    '2025-03-10T18:30'
);

const minutes = start.until(
    end,
    ChronoUnit.MINUTES
);

console.log(minutes);
630