Сравнение с нативными методами

Нативный объект Date в JavaScript представляет собой изменяемую (mutable) сущность, внутри которой хранится количество миллисекунд с эпохи Unix. Любая операция над датой либо изменяет существующий объект (в случае set* методов), либо возвращает новое значение через косвенные преобразования.

js-joda реализует противоположный подход: все временные объекты являются неизменяемыми. Любая операция (прибавление дней, смена часового пояса, форматирование) возвращает новый объект, не модифицируя исходный.

const d1 = new Date();
d1.setDate(d1.getDate() + 1); // мутирует исходный объект

const { LocalDate } = require('@js-joda/core');
const d2 = LocalDate.now().plusDays(1); // возвращает новый объект

Ключевое различие:

  • Date — изменяемое состояние
  • js-joda — функциональная модель с неизменяемыми значениями

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


Проблема локального времени в Date и строгая модель js-joda

Нативный Date объединяет в себе сразу несколько концепций:

  • момент времени (timestamp)
  • локальное представление
  • UTC-представление

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

const d = new Date('2024-03-10T00:00:00');
d.getDate(); // зависит от локальной таймзоны
d.getUTCDate(); // другое значение

js-joda разделяет эти концепции на разные типы:

  • Instant — абсолютный момент времени
  • LocalDateTime — дата и время без таймзоны
  • ZonedDateTime — дата и время с таймзоной
  • LocalDate — только дата
  • LocalTime — только время

Такое разделение устраняет неоднозначность:

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

const date = LocalDate.parse('2024-03-10');
const zoned = ZonedDateTime.now(ZoneId.of('Europe/Berlin'));

Аритметика дат: накопление ошибок и предсказуемость

Нативные методы Date допускают ряд неочевидных эффектов при арифметике:

const d = new Date(2024, 0, 31);
d.setMonth(d.getMonth() + 1);
// результат может быть 1 марта вместо 31 февраля

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

js-joda использует календарную модель ISO-8601 и строгое поведение:

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

const d = LocalDate.of(2024, 1, 31);
const result = d.plusMonths(1); // 2024-02-29 (корректная календарная логика)

Ключевой эффект:

  • Date скрыто корректирует некорректные даты
  • js-joda явно определяет правила календарных переходов

Часовые пояса и переходы на летнее время

Date работает в тесной связке с системным часовым поясом, что приводит к зависимости от окружения выполнения.

const d = new Date('2024-03-31T02:30:00');
// может быть "невалидным" временем из-за DST

js-joda явно моделирует зоны:

const { ZonedDateTime, ZoneId } = require('@js-joda/core');

const zdt = ZonedDateTime.of(
  2024, 3, 31, 2, 30, 0, 0,
  ZoneId.of('Europe/Moscow')
);

Преимущества подхода:

  • явная работа с DST-переходами
  • отсутствие скрытых преобразований
  • воспроизводимость на разных системах

Сравнение API: выразительность и читаемость

Нативный Date требует комбинации методов:

const d = new Date();
d.setFullYear(2025);
d.setMonth(5);
d.setDate(10);

js-joda использует цепочки иммутабельных операций:

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

const d = LocalDate.now()
  .withYear(2025)
  .withMonth(6)
  .withDayOfMonth(10);

Отличие проявляется в следующих аспектах:

  • отсутствие побочных эффектов
  • возможность композиции операций
  • более строгая типизация логики времени

Парсинг и форматирование: слабая зона Date

Нативный парсинг Date зависит от реализации движка и часто ведёт к неоднозначным результатам:

new Date('2024-01-01'); // интерпретация зависит от окружения

js-joda использует строгие ISO-форматы и явные парсеры:

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

const d = LocalDate.parse('2024-01-01');

Форматирование также отделено:

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

const formatter = DateTimeFormatter.ofPattern('dd-MM-yyyy');
d.format(formatter);

Ключевое различие:

  • Date — нестрогий и контекстный парсинг
  • js-joda — детерминированный и явный

Математика дат: сложность скрытой логики Date

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

  • переполнение дней месяца
  • переходы через DST
  • нормализация времени
const d = new Date(2024, 2, 31);
d.setDate(d.getDate() + 1);

Результат зависит от локальной системы и может варьироваться.

js-joda делает правила явными:

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

LocalDate.of(2024, 3, 31).plusDays(1);

Поведение строго определено ISO-стандартом.


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

Date не имеет встроенной модели интервалов. Все вычисления выполняются вручную:

const diff = (d1, d2) => d2 - d1;

js-joda предоставляет специализированные типы:

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

const duration = Duration.between(startInstant, endInstant);
const period = Period.between(startDate, endDate);

Разделение концепций:

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

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

Основная проблема Date — скрытые зависимости:

  • локальная таймзона
  • DST
  • различия браузеров и Node.js
  • неоднозначный парсинг строк

js-joda минимизирует неопределённость за счёт:

  • строгих типов времени
  • отсутствия скрытых преобразований
  • явного указания зоны и формата

Производительность и накладные расходы

Date оптимизирован на уровне движка JavaScript и обычно имеет меньшие накладные расходы на простые операции.

js-joda создаёт новые объекты при каждой операции, что:

  • увеличивает количество аллокаций
  • но повышает предсказуемость и безопасность

В большинстве прикладных сценариев (бизнес-логика, расписания, финансы) стоимость дополнительных объектов компенсируется снижением ошибок и упрощением поддержки.


Модель данных: унификация против разделения доменов времени

Date представляет универсальный контейнер времени без разделения смыслов.

js-joda строит доменную модель времени, где каждое понятие выделено:

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

Такое разделение устраняет необходимость ручного контроля контекста и снижает риск ошибок при преобразованиях между уровнями представления времени.