Отличия в API

В библиотеке Luxon центральная идея API строится вокруг неизменяемых объектов даты и времени, где каждое преобразование возвращает новый экземпляр, а не модифицирует существующий. Это фундаментально отличает её от встроенного объекта Date и от более ранних подходов, использовавшихся в экосистеме JavaScript для работы со временем.

Встроенный Date в JavaScript является изменяемым объектом, но его поведение не всегда очевидно: методы вроде setDate, setHours, setMonth изменяют состояние объекта напрямую. Luxon полностью отказывается от такого подхода.

Каждая операция в Luxon возвращает новый объект:

const { DateTime } = require('luxon');

const dt1 = DateTime.now();
const dt2 = dt1.plus({ days: 1 });

console.log(dt1.toISO());
console.log(dt2.toISO());

Здесь dt1 остаётся неизменным, а dt2 представляет новое значение. Это делает API предсказуемым и снижает количество побочных эффектов в приложениях.

Статические фабричные методы вместо конструктора

В отличие от Date, где используется new Date(), Luxon почти полностью избегает прямого использования конструктора. Основной способ создания объектов — статические методы:

  • DateTime.now()
  • DateTime.local()
  • DateTime.utc()
  • DateTime.fromISO()
  • DateTime.fromMillis()
  • DateTime.fromObject()

Такой подход делает намерение кода явным:

const a = DateTime.now();
const b = DateTime.utc(2026, 1, 1);
const c = DateTime.fromISO('2026-05-23T10:00:00');

В сравнении с new Date("..."), где строка может интерпретироваться по-разному в разных средах, Luxon делает различие между форматами входных данных строгим и прозрачным.

Явная работа с часовыми поясами

Одно из ключевых отличий Luxon от стандартного Date — полноценная модель временных зон. Встроенный Date всегда хранит время в UTC, но предоставляет локальное представление через системные настройки, что часто приводит к путанице.

Luxon вводит явное управление зонами через API:

const dt = DateTime.now().setZone('Europe/Berlin');
const utc = DateTime.utc();
const local = DateTime.local();

Каждый объект содержит информацию о зоне, и преобразование между ними выполняется явно:

const ny = dt.setZone('America/New_York');

Это убирает неявные преобразования, характерные для Date.

Отличие от Moment.js: неизменяемость и отказ от мутаций

Moment.js исторически использовал изменяемый API:

moment().add(1, 'day').subtract(2, 'hours');

Хотя цепочки методов выглядят удобно, они опираются на мутацию исходного объекта. Luxon сохраняет цепочечный стиль, но делает его функциональным:

const dt = DateTime.now()
  .plus({ days: 1 })
  .minus({ hours: 2 });

Каждый шаг возвращает новый экземпляр, что позволяет безопасно использовать объекты в асинхронном коде, Redux-подобных структурах и кэшировании.

Разделение понятий DateTime, Duration и Interval

В отличие от Date, который пытается охватить все аспекты времени, Luxon разделяет модель на три сущности:

  • DateTime — конкретный момент времени
  • Duration — длительность
  • Interval — промежуток между двумя моментами

Такое разделение радикально меняет API:

const start = DateTime.local(2026, 1, 1);
const end = DateTime.local(2026, 2, 1);

const interval = Interval.fromDateTimes(start, end);
const duration = interval.toDuration();

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

Форматирование: отказ от шаблонов Moment.js

Moment.js широко использовал строковые шаблоны формата:

moment().format("YYYY-MM-DD HH:mm");

Luxon разделяет форматирование на два подхода:

  • toFormat() — гибкие кастомные форматы
  • toLocaleString() — локализованные форматы через Intl API
dt.toFormat('yyyy LLL dd');
dt.toLocaleString(DateTime.DATETIME_MED);

Это отличие важно: Luxon делает акцент на стандартах ECMAScript Internationalization API, а не на собственных строковых интерпретаторах.

Строгий парсинг входных данных

Встроенный Date допускает неявный парсинг строк, который зависит от движка:

new Date("2026-05-23 10:00")

Luxon требует явного указания формата:

DateTime.fromISO("2026-05-23T10:00:00");
DateTime.fromFormat("23-05-2026", "dd-MM-yyyy");

Если формат не совпадает, объект становится невалидным, что можно проверить:

const dt = DateTime.fromISO("invalid");
console.log(dt.isValid);

Такой подход устраняет скрытые ошибки парсинга.

Отсутствие перегруженного конструктора

В отличие от многих библиотек прошлого поколения, Luxon избегает перегруженных конструкторов. Вместо одного универсального входа используется набор специализированных методов:

  • ISO → fromISO
  • RFC → fromRFC2822
  • Unix timestamp → fromSeconds, fromMillis
  • Object → fromObject

Это уменьшает неоднозначность API и делает код более читаемым.

Работа с локалями через Intl вместо ручных настроек

Luxon опирается на встроенный Intl API, что меняет подход к локализации по сравнению с Moment.js.

dt.setLocale('ru').toLocaleString(DateTime.DATE_FULL);

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

Разница в арифметике времени

В Date арифметика выполняется через миллисекунды:

const d = new Date();
d.setTime(d.getTime() + 86400000);

В Luxon используются семантические единицы:

dt.plus({ days: 1 });
dt.minus({ weeks: 2, hours: 3 });

Это делает код ближе к предметной области и исключает необходимость ручного пересчёта миллисекунд.

Явные свойства вместо методов-мутаторов

Встроенный Date использует методы типа getFullYear(), getMonth(). Luxon использует свойства через геттеры:

dt.year
dt.month
dt.day
dt.hour

Это делает API более компактным и читаемым, убирая избыточность вызовов функций.

Отсутствие скрытых глобальных настроек

Moment.js часто полагался на глобальное состояние:

moment.locale('ru');

Luxon избегает глобальных эффектов. Все настройки привязаны к конкретному экземпляру:

dt.setLocale('ru');
dt.setZone('Europe/Moscow');

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

Различие в модели времени: UTC как первичный слой

В Luxon UTC рассматривается как базовая координата, но не навязывается как единственное представление. В отличие от Date, где UTC скрыт за локальными преобразованиями, Luxon позволяет явно переключаться:

DateTime.utc(2026, 5, 23);
dt.toUTC();
dt.toLocal();

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

Прозрачность сериализации

Встроенный Date при сериализации автоматически превращается в строку ISO:

JSON.stringify(new Date());

Luxon требует явного контроля:

dt.toISO();
dt.toJSON();

Это исключает неожиданные форматы при передаче данных между сервисами.

Единая модель цепочек методов

Несмотря на функциональную природу, Luxon сохраняет цепочечный стиль, но делает его строго детерминированным:

DateTime.now()
  .setZone('UTC')
  .plus({ days: 2 })
  .setLocale('en')
  .toFormat('yyyy-MM-dd');

Каждый метод возвращает новый объект с чётко определённым состоянием, без скрытых побочных эффектов и мутаций исходного значения.