В библиотеке js-joda неизменяемость (immutability) является фундаментальным принципом проектирования. Любая операция над датой или временем не модифицирует исходный объект, а возвращает новый. В таких условиях классический подход «создать объект и постепенно наполнять его полями» невозможен в прямом виде. Вместо этого используется функционально-билдерный стиль, в котором состояние накапливается через цепочки преобразований.
Builder-подход в js-joda проявляется не как единый класс
Builder, а как совокупность API, поддерживающих пошаговое
конструирование через цепочки вызовов, корректировки и композицию
временных объектов.
Каждый временной тип в js-joda — LocalDate,
LocalTime, LocalDateTime,
ZonedDateTime — является неизменяемым. Любая модификация
приводит к созданию нового экземпляра.
const date1 = LocalDate.of(2026, 1, 10);
const date2 = date1.withMonth(5);
Здесь date1 остаётся неизменным, а date2
представляет новый объект.
Такой подход естественным образом формирует «builder-like» поведение: объект постепенно трансформируется через последовательность шагов.
Наиболее распространённая форма построения сложных значений — fluent API, где каждый вызов возвращает новый экземпляр с изменённым состоянием.
withМетоды семейства with являются базовыми строительными
блоками:
const base = LocalDate.of(2026, 1, 1);
const updated = base
.withYear(2027)
.withMonth(12)
.withDayOfMonth(25);
Каждый шаг формирует промежуточный объект, что концептуально эквивалентно builder, который поэтапно устанавливает поля.
plus и minusДополнительный уровень построения — смещение временных значений:
const time = LocalDateTime
.of(2026, 5, 1, 10, 30)
.plusDays(10)
.minusHours(3);
Здесь происходит не просто установка значений, а накопление изменений, что усиливает builder-ориентированную модель.
Важная особенность js-joda заключается в автоматической нормализации значений. При «построении» даты через последовательные операции система сама корректирует выход за границы диапазонов:
const dt = LocalDateTime
.of(2026, 1, 31, 23, 0)
.plusMonths(1);
Результат учитывает различную длину месяцев и корректирует итоговое значение без ручного управления состоянием.
Отдельный слой построения временных значений реализуется через
Period и Duration. Эти структуры выступают как
конфигурируемые строительные блоки.
const period = Period.ofYears(1).withMonths(2).withDays(10);
const result = LocalDate.of(2026, 1, 1).plus(period);
Здесь Period выступает как промежуточный builder,
накапливающий параметры до применения к дате.
const duration = Duration.ofHours(5).plusMinutes(30);
const result = LocalTime.of(10, 0).plus(duration);
Состояние Duration формируется пошагово и затем
применяется к временной сущности.
Одним из наиболее выразительных механизмов builder-подхода является
TemporalAdjuster. Это функциональный объект, который
трансформирует дату по заданному правилу.
const nextMonday = TemporalAdjusters.next(DayOfWeek.MONDAY);
const result = LocalDate.of(2026, 5, 25).with(nextMonday);
Здесь сам adjuster выступает как инкапсулированный строитель логики изменения даты. Он не хранит результат, но описывает шаг трансформации.
Наиболее прямое воплощение паттерна Builder в js-joda реализовано в
модуле форматирования через DateTimeFormatterBuilder.
Он позволяет поэтапно собирать форматтер, добавляя элементы форматирования в цепочке вызовов.
const formatter = new DateTimeFormatterBuilder()
.appendLiteral('Дата: ')
.appendValue(ChronoField.DAY_OF_MONTH)
.appendLiteral('.')
.appendValue(ChronoField.MONTH_OF_YEAR)
.appendLiteral('.')
.appendValue(ChronoField.YEAR)
.toFormatter();
Каждый вызов расширяет внутреннее описание форматтера, формируя сложную структуру без необходимости вручную управлять конфигурацией.
Builder форматтера поддерживает ветвления, что делает его более мощным по сравнению с простыми цепочками:
const formatter = new DateTimeFormatterBuilder()
.appendOptional(
new DateTimeFormatterBuilder()
.appendLiteral('ISO: ')
.appendInstant()
)
.toFormatter();
Такая структура позволяет собирать гибкие правила форматирования без дублирования кода.
Одной из характерных особенностей js-joda является возможность комбинировать разные уровни builder-подобных конструкций:
DateTimeFormatterBuilder формирует формат выводаPeriod и Duration формируют временные
смещенияwith и plus/minus формируют итоговые
значенияconst period = Period.ofMonths(6).withDays(15);
const formatter = new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR)
.appendLiteral('-')
.appendValue(ChronoField.MONTH_OF_YEAR)
.toFormatter();
const date = LocalDate.of(2026, 1, 1)
.plus(period)
.withDayOfMonth(10);
Каждый слой выполняет роль отдельного builder-а в общей системе построения временных вычислений.
Builder-подход в js-joda тесно связан с отложенными вычислениями. Хотя цепочки вызовов выглядят последовательными, реальные вычисления происходят на каждом шаге создания нового объекта, а не в момент определения цепочки.
Это обеспечивает:
В отличие от классического builder-паттерна, где объект мутирует
внутри билдера до вызова build(), js-joda использует
функциональный эквивалент:
const result = LocalDate.of(2026, 1, 1)
.withMonth(3)
.plusDays(10)
.withYear(2027);
Такой стиль ближе к функциональному программированию, чем к классическим объектным конструкциям, но сохраняет структуру builder-подхода.
Builder-паттерн в js-joda не является единым механизмом. Он распределён по нескольким слоям API и проявляется через:
TemporalAdjuster)Эта модель обеспечивает единый принцип: сложные временные структуры формируются постепенно, через композицию малых неизменяемых операций.