Арифметические операции

Библиотека Js-joda реализует API, основанный на концепциях Java Time API из Java 8. Арифметические операции в библиотеке строятся вокруг принципа неизменяемости объектов: любой вызов метода возвращает новый экземпляр даты или времени, не изменяя исходный объект.

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

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

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

const nextDay = date.plusDays(1);

console.log(date.toString());     // 2025-05-10
console.log(nextDay.toString());  // 2025-05-11

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


Прибавление и вычитание дней

Методы plusDays и minusDays

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

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

const date = LocalDate.of(2025, 1, 15);

console.log(date.plusDays(10).toString());
console.log(date.minusDays(5).toString());

Результат:

2025-01-25
2025-01-10

Переход между месяцами и годами

Js-joda автоматически корректно обрабатывает границы месяцев и лет.

const date = LocalDate.of(2025, 12, 28);

console.log(date.plusDays(10).toString());

Результат:

2026-01-07

Работа с високосными годами

const leap = LocalDate.of(2024, 2, 28);

console.log(leap.plusDays(1).toString());
console.log(leap.plusDays(2).toString());

Результат:

2024-02-29
2024-03-01

Библиотека автоматически учитывает календарные правила ISO-8601.


Арифметика месяцев

Метод plusMonths

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

const date = LocalDate.of(2025, 1, 10);

console.log(date.plusMonths(2).toString());

Результат:

2025-03-10

Коррекция несуществующих дат

Одной из ключевых особенностей библиотеки является автоматическая нормализация дат.

const date = LocalDate.of(2025, 1, 31);

console.log(date.plusMonths(1).toString());

Результат:

2025-02-28

Так как 31 февраля не существует, библиотека корректирует дату до последнего допустимого дня месяца.


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

const date = LocalDate.of(2025, 5, 15);

console.log(date.minusMonths(3).toString());

Результат:

2025-02-15

Арифметика лет

Методы plusYears и minusYears

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

const date = LocalDate.of(2020, 6, 20);

console.log(date.plusYears(5).toString());
console.log(date.minusYears(2).toString());

Результат:

2025-06-20
2018-06-20

Работа с 29 февраля

Особое внимание следует уделять високосным датам.

const leapDay = LocalDate.of(2024, 2, 29);

console.log(leapDay.plusYears(1).toString());

Результат:

2025-02-28

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


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

Работа с LocalTime

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

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

const time = LocalTime.of(10, 30);

console.log(time.plusHours(5).toString());
console.log(time.minusMinutes(15).toString());

Результат:

15:30
10:15

Переполнение часов

const time = LocalTime.of(23, 30);

console.log(time.plusHours(2).toString());

Результат:

01:30

Время циклически переходит через полночь.


Операции с секундами и наносекундами

const time = LocalTime.of(12, 0, 30);

console.log(time.plusSeconds(45).toString());
console.log(time.plusNanos(1000).toString());

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

Работа с LocalDateTime

Класс LocalDateTime объединяет дату и время.

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

const dt = LocalDateTime.of(2025, 5, 10, 14, 30);

console.log(dt.plusDays(2).plusHours(3).toString());

Результат:

2025-05-12T17:30

Цепочки операций

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

const result = LocalDateTime.now()
    .plusYears(1)
    .minusMonths(2)
    .plusDays(15)
    .minusHours(4);

console.log(result.toString());

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

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

Для универсальной арифметики применяется метод plus().

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

const date = LocalDate.of(2025, 1, 1);

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

console.log(result.toString());

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

Наиболее распространённые значения ChronoUnit:

  • DAYS
  • WEEKS
  • MONTHS
  • YEARS
  • HOURS
  • MINUTES
  • SECONDS
  • NANOS

Пример:

const result = date.plus(2, ChronoUnit.WEEKS);

Метод minus

Метод minus() работает аналогично plus().

const result = date.minus(3, ChronoUnit.MONTHS);

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

Представление календарного периода

Класс Period используется для хранения периодов в годах, месяцах и днях.

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

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

console.log(period.toString());

Результат:

P1Y2M15D

Добавление периода к дате

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

const date = LocalDate.of(2025, 1, 10);

const period = Period.ofMonths(3);

console.log(date.plus(period).toString());

Результат:

2025-04-10

Сложные периоды

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

const result = date.plus(period);

console.log(result.toString());

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

Интервалы времени

Duration хранит интервалы времени в секундах и наносекундах.

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

const duration = Duration.ofHours(5);

console.log(duration.toString());

Результат:

PT5H

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

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

const dt = LocalDateTime.now();

const result = dt.plus(Duration.ofMinutes(90));

console.log(result.toString());

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

Period

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

  • лет
  • месяцев
  • дней

Учитывает календарные особенности.

Period.ofMonths(1)

Duration

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

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

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

Duration.ofHours(24)

Разница между датами

Метод until

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

const start = LocalDate.of(2025, 1, 1);
const end = LocalDate.of(2025, 2, 1);

console.log(start.until(end, ChronoUnit.DAYS));

Результат:

31

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

console.log(start.until(end, ChronoUnit.MONTHS));

Результат:

1

Duration между временем

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

const start = LocalTime.of(10, 0);
const end = LocalTime.of(12, 30);

const duration = Duration.between(start, end);

console.log(duration.toString());

Результат:

PT2H30M

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

Js-joda поддерживает отрицательные значения во всех арифметических методах.

const date = LocalDate.of(2025, 5, 10);

console.log(date.plusDays(-5).toString());

Результат:

2025-05-05

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

Методы plusWeeks и minusWeeks

const date = LocalDate.of(2025, 1, 1);

console.log(date.plusWeeks(3).toString());
console.log(date.minusWeeks(1).toString());

Работа с ZonedDateTime

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

Для операций с часовыми поясами используется ZonedDateTime.

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

const zdt = ZonedDateTime.now(ZoneId.of('Asia/Almaty'));

console.log(zdt.plusHours(5).toString());

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

Js-joda корректно обрабатывает DST-переходы при наличии timezone-модуля.

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

const dt = LocalDateTime.of(2025, 3, 30, 1, 30);

const zoned = ZonedDateTime.of(
    dt,
    ZoneId.of('Europe/Berlin')
);

console.log(zoned.plusHours(2).toString());

Метод with вместо арифметики

Иногда требуется не прибавление значения, а замена конкретного поля.

const date = LocalDate.of(2025, 5, 10);

console.log(date.withMonth(12).toString());

Результат:

2025-12-10

Сравнение результатов арифметики

Проверка дат

const d1 = LocalDate.of(2025, 1, 1);
const d2 = d1.plusDays(10);

console.log(d2.isAfter(d1));
console.log(d1.isBefore(d2));

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

Изменение объекта «на месте»

Неверный подход:

let date = LocalDate.now();

date.plusDays(1);

console.log(date);

Дата не изменится.


Правильный вариант

date = date.plusDays(1);

Производительность арифметических операций

Js-joda оптимизирована для большого количества вычислений и значительно безопаснее встроенного объекта Date при сложной календарной логике:

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

Практические сценарии

Срок действия токена

const expiresAt = LocalDateTime.now()
    .plusMinutes(30);

Дата окончания подписки

const subscriptionEnd = LocalDate.now()
    .plusMonths(1);

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

const birthDate = LocalDate.of(1995, 6, 10);

const age = birthDate.until(
    LocalDate.now(),
    ChronoUnit.YEARS
);

console.log(age);

Планирование события

const meeting = ZonedDateTime.now(
    ZoneId.of('Asia/Almaty')
).plusWeeks(2).withHour(15).withMinute(0);