Операции с датой-временем

Библиотека js-joda реализует модель работы с датой и временем, основанную на неизменяемых (immutable) объектах. Любая операция над датой или временем не изменяет исходный объект, а возвращает новый экземпляр с результатом преобразования.

Ключевой принцип:

каждая операция = новый объект

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


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

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

  • plusX(...) — добавление
  • minusX(...) — вычитание

где X обозначает единицу времени.

Работа с датами (LocalDate)

import { LocalDate } from '@js-joda/core';

const date = LocalDate.parse('2026-05-24');

const nextWeek = date.plusDays(7);
const lastWeek = date.minusDays(7);

Основные методы:

  • plusDays(n) / minusDays(n)
  • plusWeeks(n) / minusWeeks(n)
  • plusMonths(n) / minusMonths(n)
  • plusYears(n) / minusYears(n)

Особенность календарной арифметики заключается в нормализации дат. Например, добавление месяца к 31 января приведёт к корректировке дня:

LocalDate.parse('2026-01-31').plusMonths(1);
// 2026-02-28 (или 29 в високосный год)

Операции с временем (LocalTime)

import { LocalTime } from '@js-joda/core';

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

const later = time.plusHours(2);
const earlier = time.minusMinutes(15);

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

  • часы
  • минуты
  • секунды
  • наносекунды
time.plusSeconds(90); // автоматическая нормализация

Переполнение времени корректно переносится внутри суток:

LocalTime.parse('23:50').plusMinutes(20);
// 00:10

Комбинированные операции (LocalDateTime)

LocalDateTime объединяет дату и время и поддерживает полную арифметику календаря и времени.

import { LocalDateTime } from '@js-joda/core';

const dt = LocalDateTime.parse('2026-05-24T10:30');

const updated = dt
  .plusDays(1)
  .plusHours(3)
  .minusMinutes(15);

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

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

Продолжительность и точная временная арифметика

Для точных временных интервалов используется Duration.

import { Duration } from '@js-joda/core';

const duration = Duration.ofHours(5).plusMinutes(30);

Применение к времени:

const result = LocalTime.parse('08:00').plus(duration);

Основные методы Duration

  • ofDays(n)
  • ofHours(n)
  • ofMinutes(n)
  • ofSeconds(n)
  • ofMillis(n)

Периоды календарных значений

Period используется для работы с датами в календарных единицах (годы, месяцы, дни).

import { Period } from '@js-joda/core';

const period = Period.ofMonths(2).plusDays(10);

Применение:

LocalDate.parse('2026-01-01').plus(period);

Различие между Period и Duration:

  • Period → календарная логика (месяцы, годы)
  • Duration → точное время (секунды, наносекунды)

Сравнение дат и времени

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

  • isBefore
  • isAfter
  • isEqual
const a = LocalDate.parse('2026-01-01');
const b = LocalDate.parse('2026-06-01');

a.isBefore(b); // true
b.isAfter(a);  // true

Сравнение учитывает тип объекта:

  • LocalDate сравнивает только дату
  • LocalTime — только время
  • LocalDateTime — оба компонента

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

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

Разница в днях

const start = LocalDate.parse('2026-01-01');
const end = LocalDate.parse('2026-01-10');

const days = start.until(end).getDays();

Разница в единицах

start.until(end, ChronoUnit.DAYS);

Работа с ChronoUnit

ChronoUnit определяет единицы измерения для операций разницы и округления.

import { ChronoUnit, LocalDateTime } from '@js-joda/core';

const a = LocalDateTime.parse('2026-01-01T00:00');
const b = LocalDateTime.parse('2026-01-02T12:00');

ChronoUnit.HOURS.between(a, b); // 36
ChronoUnit.DAYS.between(a, b);  // 1

Основные единицы:

  • NANOS
  • MICROS
  • MILLIS
  • SECONDS
  • MINUTES
  • HOURS
  • DAYS
  • MONTHS
  • YEARS

Округление и усечение времени

Truncate (усечение)

import { LocalTime, ChronoUnit } from '@js-joda/core';

LocalTime.parse('10:45:33').truncatedTo(ChronoUnit.MINUTES);
// 10:45

Усечение обнуляет младшие единицы.


Манипуляции с датой через Temporal Adjusters

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

Последний день месяца

import { TemporalAdjusters, LocalDate } from '@js-joda/core';

LocalDate.parse('2026-01-10')
  .with(TemporalAdjusters.lastDayOfMonth());

Первый день года

LocalDate.parse('2026-05-24')
  .with(TemporalAdjusters.firstDayOfYear());

Следующий понедельник

LocalDate.parse('2026-05-24')
  .with(TemporalAdjusters.next(DayOfWeek.MONDAY));

Метод with как основа преобразований

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

const date = LocalDate.parse('2026-05-24');

date.withMonth(1);   // замена месяца
date.withDayOfMonth(1);
date.withYear(2030);

Все операции возвращают новый объект.


Нормализация при переполнении значений

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

Переполнение месяцев

LocalDate.parse('2026-10-31').plusMonths(1);
// 2026-11-30

Переполнение времени

LocalTime.parse('23:59').plusMinutes(2);
// 00:01

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

Благодаря неизменяемости возможно построение цепочек преобразований:

const result = LocalDateTime.parse('2026-01-01T00:00')
  .plusMonths(2)
  .plusDays(10)
  .minusHours(3)
  .withMinute(30);

Каждый шаг возвращает новый объект, сохраняя промежуточную корректность состояния.


Инкрементальные вычисления в бизнес-логике

Операции над датой часто применяются в сценариях:

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

Пример расчёта срока подписки:

const start = LocalDate.now();
const end = start.plusMonths(1).minusDays(1);

Работа с границами диапазонов

Часто требуется вычисление начала и конца периода.

Начало дня

const startOfDay = LocalDateTime.now()
  .withHour(0)
  .withMinute(0)
  .withSecond(0);

Конец дня

const endOfDay = LocalDateTime.now()
  .withHour(23)
  .withMinute(59)
  .withSecond(59);

Конвертация между типами

js-joda позволяет переходить между представлениями времени:

const date = LocalDate.now();
const dateTime = date.atStartOfDay();

const time = LocalTime.now();
const dateTime2 = LocalDate.now().atTime(time);

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

Хотя LocalDateTime не содержит зоны, ZonedDateTime добавляет контекст:

import { ZonedDateTime, ZoneId } from '@js-joda/core';

const zdt = ZonedDateTime.now(ZoneId.of('Europe/Berlin'));

Операции сохраняют корректность при переходе между зонами:

zdt.plusHours(5);

Итоговая модель операций

Операции с датой-временем в js-joda строятся на трёх фундаментальных механизмах:

  • арифметические методы plus/minus
  • календарные корректоры with
  • интервальные типы Duration и Period

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