Переход с date-fns

date-fns и Luxon решают задачу работы с датами в JavaScript, но используют принципиально разные модели.

date-fns строится вокруг набора чистых функций, где дата передаётся как аргумент и возвращается новый результат. Такой подход обеспечивает:

  • максимальную предсказуемость
  • удобную tree-shaking оптимизацию
  • отсутствие состояния
  • минимальный runtime-слой

Luxon основан на объектной модели и абстракциях высокого уровня:

  • DateTime — основная сущность
  • Duration — длительность
  • Interval — промежуток между датами
  • неизменяемость объектов (immutability)

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


Модель данных: от функций к объектам

В date-fns типичный вызов выглядит так:

import { format, addDays } from "date-fns";

const result = format(addDays(new Date(), 5), "yyyy-MM-dd");

В Luxon аналогичная операция строится через цепочку методов:

import { DateTime } from "luxon";

const result = DateTime.now().plus({ days: 5 }).toFormat("yyyy-MM-dd");

Ключевое изменение в мышлении:

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

Основная сущность DateTime

В Luxon DateTime заменяет комбинацию функций из date-fns.

Создание даты:

DateTime.now();
DateTime.local(2026, 1, 10);
DateTime.fromISO("2026-01-10T10:00:00");
DateTime.fromMillis(1700000000000);

В date-fns аналогом будет постоянное использование new Date() и отдельных функций преобразования.

Особенность Luxon:

  • встроенная работа с часовыми поясами
  • строгая работа с ISO-форматами
  • единая модель представления времени

Форматирование: переход от шаблонов к методам объекта

date-fns:

format(new Date(), "dd.MM.yyyy HH:mm");

Luxon:

DateTime.now().toFormat("dd.MM.yyyy HH:mm");

На уровне синтаксиса различия минимальны, но архитектурно:

  • форматирование становится методом объекта
  • отсутствует необходимость передавать дату как аргумент
  • результат всегда основан на состоянии экземпляра

Дополнительно Luxon поддерживает локализованные форматы:

DateTime.now().setLocale("ru").toLocaleString(DateTime.DATE_FULL);

Изменение дат: immutability и цепочки

В date-fns:

addDays(date, 10);
subHours(date, 2);

В Luxon:

DateTime.now().plus({ days: 10 }).minus({ hours: 2 });

Ключевое отличие:

  • date-fns: функции преобразуют входной объект
  • Luxon: методы возвращают новый DateTime

Это упрощает построение цепочек трансформаций:

const result = DateTime.now()
  .plus({ days: 3 })
  .setZone("Europe/Paris")
  .startOf("day");

Работа с таймзонами

Одна из главных причин перехода на Luxon — полноценная поддержка часовых поясов.

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

Luxon включает их нативно:

DateTime.now().setZone("Asia/Almaty");

Конвертация между зонами:

const utc = DateTime.now().toUTC();
const local = utc.setZone("Europe/London");

Особенности модели:

  • дата хранится с привязкой к зоне
  • возможна точная конвертация между регионами
  • поддержка IANA time zone database

Разница в парсинге дат

date-fns:

parse("2026-01-10", "yyyy-MM-dd", new Date());

Luxon:

DateTime.fromFormat("2026-01-10", "yyyy-MM-dd");

Или:

DateTime.fromISO("2026-01-10");

Сильная сторона Luxon:

  • строгая работа с форматами
  • явное указание метода парсинга
  • меньше неоднозначностей

Duration и Interval вместо ручной арифметики

В date-fns длительности выражаются через функции:

addMinutes(date, 30);
differenceInHours(a, b);

Luxon вводит отдельные сущности.

Duration:

const d = Duration.fromObject({ hours: 2, minutes: 30 });

Interval:

const i = Interval.fromDateTimes(start, end);

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

i.length("hours");
i.contains(DateTime.now());

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

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

Сравнение API по ключевым операциям

Получение текущей даты

date-fns:

new Date();

Luxon:

DateTime.now();

Прибавление времени

date-fns:

addDays(date, 5);

Luxon:

date.plus({ days: 5 });

Форматирование

date-fns:

format(date, "yyyy-MM-dd");

Luxon:

date.toFormat("yyyy-MM-dd");

Разница дат

date-fns:

differenceInDays(a, b);

Luxon:

a.diff(b, "days").days;

Миграция: типовые соответствия

add/sub функции

date-fns Luxon
addDays plus({ days })
subDays minus({ days })
addHours plus({ hours })

format

Все шаблоны сохраняются, но вызываются через метод:

date.toFormat("pattern");

parse

Разделение по типам входных данных:

  • ISO → fromISO
  • Unix timestamp → fromMillis
  • пользовательские форматы → fromFormat

Работа с локалями

date-fns:

import { ru } from "date-fns/locale";
format(date, "PPPP", { locale: ru });

Luxon:

DateTime.now().setLocale("ru").toLocaleString(DateTime.DATE_FULL);

Особенность Luxon:

  • локаль устанавливается на объект
  • форматирование отделено от шаблонов
  • встроенные пресеты форматов

Ошибки миграции и скрытые различия

1. Переход на объектную модель

Частая проблема — сохранение функционального стиля:

// неэффективный стиль в Luxon
plus(addDays(DateTime.now(), 5))

Правильный подход:

DateTime.now().plus({ days: 5 });

2. Игнорирование immutability

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

let dt = DateTime.now();
dt.plus({ days: 1 }); // результат теряется

Корректно:

dt = dt.plus({ days: 1 });

3. Разница в форматах

date-fns использует более гибкий парсер шаблонов, Luxon — строгий набор токенов.

Ошибки вида:

  • неправильные символы формата
  • несовпадение ISO и custom format

4. Таймзоны как источник расхождений

date-fns часто работает в локальной зоне окружения, Luxon требует явного управления зонами:

DateTime.now().setZone("UTC");

Bundle size и архитектурные компромиссы

date-fns оптимизирован под tree-shaking:

  • импортируются только используемые функции
  • минимальный runtime

Luxon:

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

Компромисс при переходе:

  • рост bundle size
  • снижение сложности бизнес-логики дат
  • уменьшение числа вспомогательных библиотек

Типичные сценарии перехода

1. Усложнение работы с датами

Когда логика начинает включать:

  • таймзоны
  • интервалы
  • календарные расчёты
  • локализацию

Luxon становится более естественной моделью.


2. Централизация работы с временем

Вместо набора функций появляется единый доменный объект DateTime, что снижает фрагментацию кода.


3. Отказ от смешивания библиотек

В некоторых проектах date-fns используется вместе с moment.js-подобными решениями. Переход на Luxon упрощает архитектуру за счёт единого API.


Переписывание реальных паттернов

Паттерн: диапазон дат

date-fns:

isWithinInterval(date, { start, end });

Luxon:

Interval.fromDateTimes(start, end).contains(date);

Паттерн: конец дня

date-fns:

endOfDay(date);

Luxon:

date.endOf("day");

Паттерн: начало месяца

date-fns:

startOfMonth(date);

Luxon:

date.startOf("month");

Итоговые изменения в мышлении при переходе

Переход с date-fns на Luxon приводит к смене парадигмы:

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

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