Методы Period и Duration

Period в js-joda представляет календарную длительность, выраженную в годах, месяцах и днях. Такой тип интервала зависит от календарной системы и не фиксирован в абсолютных секундах: один месяц может содержать разное число дней, один год — 365 или 366 дней.

Создание Period

Основной набор фабричных методов:

  • Period.of(years, months, days)
  • Period.ofYears(years)
  • Period.ofMonths(months)
  • Period.ofWeeks(weeks)
  • Period.ofDays(days)

Примеры:

Period.of(1, 2, 10)   // 1 год, 2 месяца, 10 дней
Period.ofYears(3)     // 3 года
Period.ofMonths(6)    // 6 месяцев
Period.ofWeeks(2)     // 14 дней (нормализуется в дни)
Period.ofDays(5)      // 5 дней

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

Разбор строкового представления

Метод Period.parse() разбирает ISO-8601 строку:

Period.parse("P1Y2M3D")
Period.parse("P10D")
Period.parse("P2W") // 14 дней

Формат строго соответствует ISO:

  • P — префикс периода
  • Y — годы
  • M — месяцы
  • W — недели
  • D — дни

Доступ к компонентам периода

Методы чтения внутренних значений:

  • getYears()
  • getMonths()
  • getDays()
const p = Period.of(2, 5, 12);

p.getYears();  // 2
p.getMonths(); // 5
p.getDays();   // 12

Важно учитывать, что Period не нормализует месяцы в годы автоматически, кроме случаев явного вызова normalized().

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

Метод normalized() приводит месяцы к диапазону 0–11, перераспределяя их в годы:

Period.of(1, 15, 0).normalized()
// 2 года, 3 месяца, 0 дней

Логика основана на том, что 12 месяцев = 1 год.

Арифметика Period

Поддерживаются операции:

  • plus(otherPeriod)
  • minus(otherPeriod)
  • multipliedBy(scalar)
  • negated()
const p1 = Period.of(1, 2, 10);
const p2 = Period.of(0, 3, 5);

p1.plus(p2);      // 1Y 5M 15D
p1.minus(p2);     // 1Y -1M 5D
p1.multipliedBy(2); // 2Y 4M 20D
p1.negated();     // -1Y -2M -10D

Проверки состояния

  • isZero() — период равен нулю
  • isNegative() — хотя бы один компонент отрицательный
Period.of(0, 0, 0).isZero(); // true
Period.of(0, -1, 0).isNegative(); // true

Взаимодействие с датами

Period применяется к LocalDate:

  • plus(period)
  • minus(period)
  • plusYears, plusMonths, plusDays как эквиваленты через Period
date.plus(Period.ofMonths(2))
date.minus(Period.ofDays(10))

Поведение зависит от календарной логики: добавление месяцев может менять день месяца.


Duration: работа с точным временем

Duration представляет точный временной интервал, основанный на фиксированных единицах времени: секунды и наносекунды. В отличие от Period, он не зависит от календаря.

Создание Duration

Основные фабричные методы:

  • Duration.ofDays(days)
  • Duration.ofHours(hours)
  • Duration.ofMinutes(minutes)
  • Duration.ofSeconds(seconds)
  • Duration.ofMillis(millis)
  • Duration.ofNanos(nanos)
Duration.ofHours(5)
Duration.ofMinutes(90)
Duration.ofSeconds(30)
Duration.ofMillis(1500)

Все методы преобразуются в секунды и наносекунды внутри.

Парсинг ISO-8601

Duration.parse("PT20S")
Duration.parse("PT15M")
Duration.parse("PT1H")
Duration.parse("PT2H30M")

Формат:

  • P — префикс периода
  • T — начало временной части
  • H, M, S — часы, минуты, секунды

Доступ к компонентам

  • getSeconds()
  • getNano()
const d = Duration.ofSeconds(5, 500000000);

d.getSeconds(); // 5
d.getNano();    // 500000000

Важно: Duration хранит нормализованное значение, где наносекунды всегда в диапазоне 0–999,999,999.

Арифметика Duration

Поддерживаются операции:

  • plus(duration)
  • minus(duration)
  • multipliedBy(scalar)
  • dividedBy(divisor)
  • negated()
  • abs()
const d1 = Duration.ofMinutes(10);
const d2 = Duration.ofSeconds(30);

d1.plus(d2);        // PT10M30S
d1.minus(d2);       // PT9M30S
d1.multipliedBy(2); // PT20M
d1.dividedBy(2);    // PT5M
d1.negated();       // отрицательная длительность
d1.abs();           // абсолютное значение

Проверки состояния

  • isZero() — нулевая длительность
  • isNegative() — отрицательная длительность
Duration.ofSeconds(0).isZero(); // true
Duration.ofSeconds(-5).isNegative(); // true

Преобразование единиц

  • toMillis()
  • toSeconds()
  • toMinutes()
  • toHours()
Duration.ofSeconds(90).toMinutes(); // 1 (с округлением вниз)
Duration.ofMillis(1500).toSeconds(); // 1

Преобразования выполняются с усечением дробной части.

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

Duration часто применяется к Instant:

  • plus(duration)
  • minus(duration)
  • plusSeconds, plusMillis и т.д. через Duration
instant.plus(Duration.ofHours(1))
instant.minus(Duration.ofMinutes(15))

Также используется в вычислении разницы:

  • Duration.between(start, end)
Duration.between(instant1, instant2)

Результат может быть отрицательным, если второй момент раньше первого.


Сравнение Period и Duration в модели js-joda

Семантика

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

Зависимость от календаря

  • Period зависит от календарной структуры
  • Duration независим от календаря

Типичные сценарии

Period:

  • добавление месяцев к дате рождения
  • расчёт возрастных интервалов
  • календарные смещения

Duration:

  • измерение времени выполнения
  • работа с таймерами
  • вычисления между Instant

Особенности поведения

Добавление Period к дате:

LocalDate.of(2024, 1, 31).plus(Period.ofMonths(1))
// может дать 2024-02-29 или 2024-02-28

Добавление Duration к времени:

Instant.now().plus(Duration.ofSeconds(30))

Результат всегда детерминирован и не зависит от календаря.


Внутренняя арифметика и нормализация

Period

  • Не приводит компоненты к единому масштабу автоматически
  • Исключение — normalized()
  • Возможны отрицательные значения в отдельных полях

Duration

  • Всегда хранится в нормализованной форме
  • Секунды и наносекунды приводятся к единой системе
  • Арифметика всегда линейна

Использование в цепочках вычислений

Period

date
  .plus(Period.ofMonths(2))
  .plus(Period.ofDays(10))
  .minus(Period.ofYears(1))

Каждая операция изменяет календарную позицию, сохраняя относительную структуру.

Duration

instant
  .plus(Duration.ofSeconds(30))
  .plus(Duration.ofMinutes(5))
  .minus(Duration.ofMillis(500))

Все операции эквивалентны суммированию фиксированных временных единиц.


Особенности отрицательных значений

Period

Отрицательность может быть неоднородной:

Period.of(1, -2, 3)

Каждое поле интерпретируется отдельно.

Duration

Отрицательность глобальна:

Duration.ofSeconds(-10)

Нельзя иметь «частично отрицательную» длительность — знак распространяется на всё значение.


Роль в архитектуре js-joda

Period и Duration разделяют модель времени на два слоя:

  • календарный слой (LocalDate, Period)
  • временной слой (Instant, Duration)

Такое разделение устраняет неоднозначности, характерные для традиционных Date API в JavaScript, где месяцы, дни и часы смешаны в одной модели без строгой семантики.