Библиотека построена вокруг принципа атомарных функций: каждая операция над датой реализуется как независимая функция без скрытого состояния. Такой подход делает возможной естественную композицию — объединение нескольких простых преобразований в единый поток обработки данных.
Каждая функция решает одну задачу:
addDays,
subMonths)startOfDay, endOfMonth)isBefore, isEqual)format)parse, parseISO)Благодаря этому результат одной функции может быть напрямую передан в другую, формируя цепочку преобразований.
Композиция функций строится на идее последовательного применения преобразований к значению.
Общая форма:
f(g(h(x)))
В контексте работы с датами это означает последовательное
преобразование объекта Date или строки даты через набор
операций.
Для упрощения часто используется функция-композитор:
const compose = (...fns) => (value) =>
fns.reduceRight((acc, fn) => fn(acc), value);
Или вариант слева направо:
const pipe = (...fns) => (value) =>
fns.reduce((acc, fn) => fn(acc), value);
Функции библиотеки легко комбинируются без дополнительной обёртки.
Пример последовательного преобразования даты:
import { addDays, startOfDay, format } from 'date-fns';
const result = format(
startOfDay(addDays(new Date(2024, 0, 1), 5)),
'yyyy-MM-dd'
);
Здесь происходит цепочка:
Такая структура уже является композиционной, но становится менее читаемой при росте количества операций.
При увеличении количества преобразований используется явная композиция:
import { addDays, startOfDay, format } from 'date-fns';
const pipe = (...fns) => (value) =>
fns.reduce((acc, fn) => fn(acc), value);
const transformDate = pipe(
(date) => addDays(date, 10),
startOfDay,
(date) => format(date, 'dd.MM.yyyy')
);
const result = transformDate(new Date());
Особенность заключается в том, что каждая функция становится частью декларативного конвейера обработки даты.
Для функционального стиля в библиотеке существует отдельный подмодуль
date-fns/fp, где функции:
Пример:
import { pipe } from 'date-fns/fp';
import { addDays, startOfDay, format } from 'date-fns/fp';
const transform = pipe(
addDays(10),
startOfDay,
format('yyyy-MM-dd')
);
const result = transform(new Date());
Ключевое отличие:
addDays(date, amount)addDays(amount)(date)Это позволяет строить чистые цепочки без обёрток и лямбда-функций.
Частый сценарий — приведение даты к единому формату перед обработкой.
import { pipe } from 'date-fns/fp';
import { parseISO, startOfDay, endOfDay } from 'date-fns/fp';
const normalizeStart = pipe(
parseISO,
startOfDay
);
const normalizeEnd = pipe(
parseISO,
endOfDay
);
Такая композиция позволяет стандартизировать входные данные независимо от их исходного представления.
Композиция особенно эффективна при создании повторно используемых трансформаций.
Пример расчёта даты дедлайна:
import { pipe } from 'date-fns/fp';
import { addDays, setHours, setMinutes } from 'date-fns/fp';
const buildDeadline = pipe(
addDays(7),
setHours(18),
setMinutes(0)
);
const deadline = buildDeadline(new Date());
Здесь каждая функция последовательно уточняет результат:
При необходимости условных преобразований композиция расширяется через функции-адаптеры:
import { pipe } from 'date-fns/fp';
import { addDays, format } from 'date-fns/fp';
const addWeekendBonus = (date) =>
date.getDay() === 5 ? addDays(3)(date) : date;
const process = pipe(
addWeekendBonus,
addDays(1),
format('yyyy-MM-dd')
);
Функции, не являющиеся частью date-fns, легко встраиваются в pipeline, сохраняя единый стиль обработки.
date-fns предоставляет функции для работы с расстояниями между датами, которые также хорошо комбинируются.
import { pipe } from 'date-fns/fp';
import { differenceInDays, abs } from 'date-fns/fp';
const getPositiveDiff = pipe(
differenceInDays(new Date())
);
При необходимости можно строить более сложные вычисления:
const normalizeDiff = (baseDate) => pipe(
differenceInDays(baseDate),
Math.abs
);
Композиция здесь используется для построения параметризованных вычислений.
Часто требуется объединить несколько этапов обработки: разбор строки, нормализация, преобразование и форматирование.
import { pipe } from 'date-fns/fp';
import { parseISO, addMonths, startOfMonth, format } from 'date-fns/fp';
const processInvoiceDate = pipe(
parseISO,
addMonths(1),
startOfMonth,
format('yyyy-MM-dd')
);
Такая структура превращает обработку даты в линейный декларативный процесс, где каждый шаг независим и предсказуем.
В прикладных сценариях композиция позволяет описывать бизнес-правила через последовательность операций над датой.
Пример формирования периода отчётности:
import { pipe } from 'date-fns/fp';
import { subDays, startOfDay, endOfDay, format } from 'date-fns/fp';
const buildReportRange = pipe(
subDays(30),
startOfDay,
endOfDay,
format('yyyy-MM-dd')
);
Каждая функция выражает отдельный смысловой шаг, а вся цепочка формирует целостную модель преобразования.
При активной композиции функций проявляются характерные особенности:
DateОсобое внимание требуется при смешивании:
date-fnsdate-fns/fpИх сигнатуры несовместимы без адаптации.
Функциональная композиция превращает набор утилитарных операций в единые вычислительные конструкции. Вместо разрозненных вызовов формируется поток обработки данных, в котором каждая функция выполняет строго ограниченную роль и не зависит от контекста выполнения.
Такая организация кода особенно эффективна при повторяющихся сценариях преобразования дат, где важна предсказуемость и воспроизводимость результата.