Одной из ключевых причин появления js-joda стала
фундаментальная проблема встроенного объекта Date в
JavaScript — его мутабельность и связанные с этим побочные эффекты при
передаче, копировании и преобразовании значений времени.
В стандартной модели Date представляет собой изменяемый
объект: любые операции модификации меняют сам экземпляр. В
противоположность этому, типы js-joda реализуют
неизменяемую (immutable) модель, где каждая операция возвращает новый
объект, не затрагивая исходный.
При конвертации между этими двумя моделями возникает критическая точка, в которой часто появляются ошибки, связанные с неожиданным изменением состояния или потерей точности.
Date как источника мутабельностиВстроенный Date позволяет изменять своё состояние через
набор методов:
const d = new Date(2024, 0, 1);
d.setFullYear(2025);
d.setMonth(5);
После выполнения этих операций исходный объект уже содержит новое значение времени. Это означает:
Даже операции, которые выглядят как «получение нового значения», часто скрыто мутируют объект:
const d = new Date(2024, 0, 1);
const copy = d;
copy.setDate(10);
console.log(d); // уже изменён
Такое поведение создаёт проблемы при работе с временными вычислениями, особенно в сложных доменных моделях.
js-jodaВ js-joda все классы времени (например,
LocalDate, LocalDateTime,
Instant) являются неизменяемыми.
import { LocalDate } from '@js-joda/core';
const date = LocalDate.of(2024, 1, 1);
const upd ated = date.plusDays(10);
console.log(date.toString()); // 2024-01-01
console.log(upd ated.toString()); // 2024-01-11
Ключевая характеристика:
Конвертация между Date и js-joda выглядит
простой, но именно здесь возникает наиболее частый источник ошибок.
Date в
LocalDateimport { LocalDate } from '@js-joda/core';
const jsDate = new Date(2024, 0, 15);
const localDate = LocalDate.of(
jsDate.getFullYear(),
jsDate.getMonth() + 1,
jsDate.getDate()
);
На этом этапе важно понимать:
Date может быть уже изменён ранее в коде;Date не влияют на
LocalDate.function toLocalDate(date) {
date.setDate(date.getDate() + 1); // побочный эффект
return LocalDate.of(
date.getFullYear(),
date.getMonth() + 1,
date.getDate()
);
}
Проблема:
Такое поведение особенно опасно в асинхронных системах и при работе с кэшированием.
js-joda → DateПри преобразовании из неизменяемого объекта в мутабельный требуется явное создание нового экземпляра:
import { LocalDate } from '@js-joda/core';
const localDate = LocalDate.of(2024, 1, 15);
const jsDate = new Date(
localDate.year(),
localDate.monthValue() - 1,
localDate.dayOfMonth()
);
Здесь важно учитывать:
Date создаётся как независимый объект;LocalDate не сохраняется;Date не влияют на
js-joda объект.Смешивание Date и js-joda в одном коде
часто приводит к скрытым ошибкам.
const cache = new Map();
function getDate(key, date) {
cache.se t(key, date);
return LocalDate.of(
date.getFullYear(),
date.getMonth() + 1,
date.getDate()
);
}
Проблема заключается в том, что в cache сохраняется
ссылка на изменяемый объект. Если позже date будет изменён,
значение в кэше также «изменится».
function getDate(key, date) {
const snapshot = new Date(date.getTime());
cache.se t(key, snapshot);
return LocalDate.of(
snapshot.getFullYear(),
snapshot.getMonth() + 1,
snapshot.getDate()
);
}
Здесь создаётся независимая копия, исключающая влияние внешних изменений.
Date всегда хранит момент времени в UTC, но отображает
его в локальной временной зоне. Это добавляет ещё один уровень
неопределённости при конвертации.
const d = new Date('2024-01-01T00:00:00Z');
При извлечении компонентов:
d.getDate();
d.getMonth();
значения зависят от локальной зоны исполнения.
В js-joda модель разделяет:
LocalDate — календарная дата без зоны;Instant — абсолютный момент времени;ZonedDateTime — дата-время с зоной.Это устраняет неоднозначность, но требует аккуратной конвертации.
Одной из наиболее распространённых проблем является передача
Date в функции, которые не документируют своё
поведение.
function shiftByDay(date) {
date.setDate(date.getDate() + 1);
return date;
}
Вызов:
const d = new Date();
const result = shiftByDay(d);
console.log(d === result); // true
Вся система начинает зависеть от скрытых изменений.
При работе с js-joda и Date одновременно
используется принцип:
мутабельные объекты — только на границах системы
Внутри бизнес-логики:
js-joda;Пример:
function process(inputDate) {
const local = LocalDate.of(
inputDate.getFullYear(),
inputDate.getMonth() + 1,
inputDate.getDate()
);
const result = local.plusDays(7);
return new Date(
result.year(),
result.monthValue() - 1,
result.dayOfMonth()
);
}
Такой подход гарантирует:
Date отражает императивную модель времени:
js-joda реализует функциональный подход:
Конвертация между ними — это не просто техническая операция, а переход между двумя различными парадигмами работы со временем.