Period для календарных периодов

Класс Period из библиотеки js-joda предназначен для представления календарного периода в терминах:

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

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

Примеры:

  • «2 года 3 месяца 5 дней»;
  • «1 месяц»;
  • «14 дней».

Подключение

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

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

Метод of

Базовый способ создания периода — метод Period.of(years, months, days).

const period = Period.of(1, 6, 15)

console.log(period.toString())

Результат:

P1Y6M15D

Формат соответствует стандарту ISO-8601:

Обозначение Значение
P начало периода
Y годы
M месяцы
D дни

Создание периода только из лет

ofYears

const period = Period.ofYears(3)

console.log(period.toString())

Результат:

P3Y

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

ofMonths

const period = Period.ofMonths(8)

console.log(period.toString())

Результат:

P8M

Создание периода только из дней

ofDays

const period = Period.ofDays(21)

console.log(period.toString())

Результат:

P21D

Нулевой период

ZERO

Константа Period.ZERO представляет пустой период.

const period = Period.ZERO

console.log(period.toString())

Результат:

P0D

Проверка:

console.log(period.isZero())

Результат:

true

Разбор строки

parse

Period.parse() преобразует ISO-строку в объект периода.

const period = Period.parse('P2Y5M10D')

console.log(period.years())
console.log(period.months())
console.log(period.days())

Результат:

2
5
10

Получение отдельных компонентов

Годы

const period = Period.of(4, 7, 12)

console.log(period.years())

Месяцы

console.log(period.months())

Дни

console.log(period.days())

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

Period чаще всего используется вместе с LocalDate.

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

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

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

const result = date.plus(period)

console.log(result.toString())

Результат:

2026-03-25

Происходит последовательное добавление:

  1. 1 год;
  2. 2 месяца;
  3. 15 дней.

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

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

const period = Period.ofMonths(3)

const result = date.minus(period)

console.log(result.toString())

Результат:

2025-05-20

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

between

Метод Period.between(start, end) вычисляет календарную разницу между датами.

const start = LocalDate.parse('2020-01-15')
const end = LocalDate.parse('2023-04-20')

const period = Period.between(start, end)

console.log(period.toString())

Результат:

P3Y3M5D

Особенности вычисления разницы

Period.between() использует календарную арифметику.

const start = LocalDate.parse('2025-01-31')
const end = LocalDate.parse('2025-02-28')

const period = Period.between(start, end)

console.log(period.toString())

Результат:

P28D

Причина заключается в том, что февраль не содержит 31 числа.


Отрицательные периоды

Period может содержать отрицательные значения.

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

console.log(period.toString())

Результат:

P-1Y-2M-10D

Проверка на отрицательный период

isNegative

const period = Period.ofDays(-5)

console.log(period.isNegative())

Результат:

true

Проверка на нулевой период

isZero

const period = Period.of(0, 0, 0)

console.log(period.isZero())

Изменение периода

Объекты Period неизменяемы.

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


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

plusYears

const period = Period.ofMonths(5)

const result = period.plusYears(2)

console.log(result.toString())

Результат:

P2Y5M

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

plusMonths

const period = Period.ofYears(1)

const result = period.plusMonths(9)

console.log(result.toString())

Результат:

P1Y9M

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

plusDays

const period = Period.ofYears(1)

const result = period.plusDays(20)

console.log(result.toString())

Результат:

P1Y20D

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

minusYears

const period = Period.ofYears(5)

const result = period.minusYears(2)

console.log(result.toString())

Результат:

P3Y

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

minusMonths

const period = Period.ofMonths(10)

const result = period.minusMonths(4)

console.log(result.toString())

Результат:

P6M

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

minusDays

const period = Period.ofDays(30)

const result = period.minusDays(12)

console.log(result.toString())

Результат:

P18D

Инвертирование периода

negated

const period = Period.of(1, 2, 3)

const result = period.negated()

console.log(result.toString())

Результат:

P-1Y-2M-3D

Абсолютное значение периода

abs

const period = Period.of(-1, -3, -15)

const result = period.abs()

console.log(result.toString())

Результат:

P1Y3M15D

Умножение периода

multipliedBy

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

const result = period.multipliedBy(3)

console.log(result.toString())

Результат:

P3Y6M30D

Нормализация периода

normalized

Метод преобразует месяцы в годы, если количество месяцев превышает 12.

const period = Period.of(1, 15, 10)

const result = period.normalized()

console.log(result.toString())

Результат:

P2Y3M10D

Важная особенность normalized

Нормализация касается только месяцев и лет.

Дни не преобразуются в месяцы.

const period = Period.of(0, 0, 90)

console.log(period.normalized().toString())

Результат:

P90D

Причина связана с тем, что длина месяца различается.


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

У Period отсутствует прямое сравнение через:

  • isBefore;
  • isAfter.

Причина заключается в неоднозначности календарных величин.

Например:

  • 1 месяц может быть 28, 29, 30 или 31 днём;
  • 1 год может содержать 365 или 366 дней.

Проверка равенства

equals

const p1 = Period.of(1, 2, 3)
const p2 = Period.of(1, 2, 3)

console.log(p1.equals(p2))

Результат:

true

Преобразование в строку

toString

const period = Period.of(2, 5, 7)

console.log(period.toString())

Результат:

P2Y5M7D

Работа с отрицательными компонентами

Допустимы смешанные значения.

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

console.log(period.toString())

Результат:

P1Y-2M15D

Подобные конструкции встречаются редко, но библиотека их поддерживает.


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

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

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

const dates = [
    LocalDate.parse('2025-01-01'),
    LocalDate.parse('2025-02-01'),
    LocalDate.parse('2025-03-01')
]

const period = Period.ofMonths(1)

const updated = dates.map(date => date.plus(period))

updated.forEach(date => {
    console.log(date.toString())
})

Генерация графика платежей

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

const paymentPeriod = Period.ofMonths(1)

for (let i = 0; i < 6; i++) {
    const paymentDate = start.plus(paymentPeriod.multipliedBy(i))

    console.log(paymentDate.toString())
}

Результат:

2025-01-15
2025-02-15
2025-03-15
2025-04-15
2025-05-15
2025-06-15

Использование для возраста

const birthDate = LocalDate.parse('1995-06-10')
const currentDate = LocalDate.parse('2026-05-01')

const age = Period.between(birthDate, currentDate)

console.log(age.years())

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

Period Duration
Календарные единицы Точное время
Годы, месяцы, дни Секунды, наносекунды
Зависит от календаря Независим от календаря
Используется с датами Используется со временем

Когда использовать Period

Period подходит для:

  • возраста;
  • подписок;
  • кредитных графиков;
  • календарных расчётов;
  • отпусков;
  • сроков действия;
  • интервалов между датами.

Когда Period использовать нельзя

Period не подходит для:

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

Для таких задач применяется Duration.


Внутреннее устройство ISO-представления

Строка:

P2Y4M6D

Расшифровывается так:

Компонент Значение
P period
2Y 2 года
4M 4 месяца
6D 6 дней

Частичные значения

Допустимо указывать только отдельные компоненты.

Period.parse('P5D')
Period.parse('P2M')
Period.parse('P1Y')

Периоды и високосные годы

const start = LocalDate.parse('2024-02-29')

const result = start.plus(Period.ofYears(1))

console.log(result.toString())

Результат:

2025-02-28

При отсутствии 29 февраля дата корректируется автоматически.


Работа с концом месяца

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

const result = date.plusMonths(1)

console.log(result.toString())

Результат:

2025-02-28

Period и методы календарной арифметики учитывают реальные границы месяцев.


Иммутабельность

Все объекты Period являются immutable-объектами.

Операции:

  • не изменяют исходный экземпляр;
  • безопасны для многопоточного окружения;
  • упрощают предсказуемость кода.

Пример:

const original = Period.ofMonths(3)

const changed = original.plusDays(5)

console.log(original.toString())
console.log(changed.toString())

Результат:

P3M
P3M5D

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

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

const subscriptionPeriod = Period.ofMonths(12)

const expirationDate = subscriptionStart.plus(subscriptionPeriod)

console.log(expirationDate.toString())

Практический сценарий: испытательный срок

const employmentDate = LocalDate.parse('2025-03-01')

const probation = Period.ofMonths(3)

const probationEnd = employmentDate.plus(probation)

console.log(probationEnd.toString())

Практический сценарий: отпуск

const vacationStart = LocalDate.parse('2025-07-01')

const vacationLength = Period.ofDays(14)

const vacationEnd = vacationStart.plus(vacationLength)

console.log(vacationEnd.toString())