Композиция существующих функций

Модульная природа date-fns как основа композиции

Библиотека построена вокруг принципа атомарных функций: каждая операция над датой реализуется как независимая функция без скрытого состояния. Такой подход делает возможной естественную композицию — объединение нескольких простых преобразований в единый поток обработки данных.

Каждая функция решает одну задачу:

  • вычисление смещения даты (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);

Композиция на основе core-функций date-fns

Функции библиотеки легко комбинируются без дополнительной обёртки.

Пример последовательного преобразования даты:

import { addDays, startOfDay, format } from 'date-fns';

const result = format(
  startOfDay(addDays(new Date(2024, 0, 1), 5)),
  'yyyy-MM-dd'
);

Здесь происходит цепочка:

  1. добавление дней
  2. приведение к началу суток
  3. форматирование результата

Такая структура уже является композиционной, но становится менее читаемой при росте количества операций.


Улучшение читаемости через pipe-композицию

При увеличении количества преобразований используется явная композиция:

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

Для функционального стиля в библиотеке существует отдельный подмодуль date-fns/fp, где функции:

  • каррированы
  • используют порядок аргументов data-last
  • оптимизированы под композицию

Пример:

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)
  • fp-версия: 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());

Здесь каждая функция последовательно уточняет результат:

  • сначала вычисляется дата через 7 дней
  • затем устанавливается фиксированное время
  • затем нормализуются минуты

Композиция с условной логикой

При необходимости условных преобразований композиция расширяется через функции-адаптеры:

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
  • необходимость контроля порядка функций
  • потенциальная потеря читаемости при чрезмерной глубине pipeline
  • важность различения обычных и fp-версий функций

Особое внимание требуется при смешивании:

  • стандартных функций date-fns
  • функций date-fns/fp

Их сигнатуры несовместимы без адаптации.


Композиция как способ структурирования логики работы с датами

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

Такая организация кода особенно эффективна при повторяющихся сценариях преобразования дат, где важна предсказуемость и воспроизводимость результата.