Обертки для специфичных задач

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


Базовая идея оберток над функциями date-fns

Функции date-fns проектируются как чистые преобразования:

  • вход: дата и параметры
  • выход: новая дата или строка
  • отсутствие побочных эффектов

Обертка сохраняет эту модель, добавляя слой логики:

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

Унификация входных данных

В прикладных системах дата может приходить в разных форматах: строка ISO, timestamp, объект Date, значение из базы данных.

Типовая обертка нормализации:

import { parseISO, isValid } from 'date-fns';

function toDate(input) {
  if (!input) return null;

  const date =
    input instanceof Date ? input :
    typeof input === 'string' ? parseISO(input) :
    typeof input === 'number' ? new Date(input) :
    null;

  if (!date || !isValid(date)) return null;

  return date;
}

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


Безопасные обертки (Safe wrappers)

Чистые функции date-fns не обрабатывают ошибки входных данных. В прикладной логике это часто приводит к каскадным сбоям.

Безопасная обертка для форматирования:

import { format } from 'date-fns';

function safeFormat(dateInput, formatString) {
  const date = toDate(dateInput);
  if (!date) return null;

  try {
    return format(date, formatString);
  } catch {
    return null;
  }
}

Такая конструкция применяется в UI-слое, где важно избегать исключений при отображении.


Обертки с дефолтными параметрами

Часто используется фиксированный формат даты или повторяющиеся настройки.

import { format } from 'date-fns';

const DEFAULT_FORMAT = 'yyyy-MM-dd';

function formatDate(dateInput, formatString = DEFAULT_FORMAT) {
  const date = toDate(dateInput);
  if (!date) return null;

  return format(date, formatString);
}

Подобный подход снижает дублирование и фиксирует стандарты отображения дат в проекте.


Композиционные обертки

Одна из сильных сторон date-fns — возможность композиции функций. Обертки позволяют создавать цепочки преобразований.

Пример: вычисление даты истечения срока с форматированием:

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

function addDaysAndFormat(dateInput, days, formatString) {
  const date = toDate(dateInput);
  if (!date) return null;

  const result = addDays(date, days);
  return format(result, formatString);
}

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


Частичное применение (Partial application)

Для повторяющихся сценариев удобно фиксировать часть параметров.

import { format } from 'date-fns';

function createFormatter(formatString) {
  return function(dateInput) {
    const date = toDate(dateInput);
    if (!date) return null;

    return format(date, formatString);
  };
}

const formatISODate = createFormatter('yyyy-MM-dd');
const formatDateTime = createFormatter('yyyy-MM-dd HH:mm:ss');

Это снижает когнитивную нагрузку в бизнес-логике и стандартизирует форматирование.


Обертки для временных зон

date-fns в базовой поставке не управляет временными зонами напрямую. Поэтому часто создаются обертки вокруг date-fns-tz.

import { formatInTimeZone } from 'date-fns-tz';

function formatUTC(dateInput, formatString) {
  const date = toDate(dateInput);
  if (!date) return null;

  return formatInTimeZone(date, 'UTC', formatString);
}

Подобная абстракция фиксирует зону времени и предотвращает ошибки смещения.


Обертки для бизнес-логики дат

В прикладных системах даты часто имеют семантику: дедлайны, периоды подписки, окна доступности.

import { isAfter, isBefore } from 'date-fns';

function isActivePeriod(dateInput, start, end) {
  const date = toDate(dateInput);
  const startDate = toDate(start);
  const endDate = toDate(end);

  if (!date || !startDate || !endDate) return false;

  return isAfter(date, startDate) && isBefore(date, endDate);
}

Такая обертка превращает низкоуровневые операции в доменные проверки.


Защитные обертки для диапазонов

Работа с датами часто требует контроля допустимых границ.

import { min, max } from 'date-fns';

function clampDate(dateInput, minDate, maxDate) {
  const date = toDate(dateInput);
  const minD = toDate(minDate);
  const maxD = toDate(maxDate);

  if (!date || !minD || !maxD) return null;

  return min([max([date, minD]), maxD]);
}

Такие конструкции предотвращают выход значений за допустимые пределы.


Обертки для работы с массивами дат

Часто требуется массовая обработка списков дат.

import { format } from 'date-fns';

function formatDateList(dates, formatString) {
  if (!Array.isArray(dates)) return [];

  return dates
    .map(toDate)
    .filter(Boolean)
    .map(date => format(date, formatString));
}

Подобная обертка стандартизирует обработку коллекций и снижает вероятность ошибок в итерациях.


Декораторы поведения (logging, profiling)

Обертки могут использоваться для наблюдения за поведением функций date-fns.

import { format } from 'date-fns';

function withLogging(fn) {
  return function(...args) {
    const start = Date.now();
    const result = fn(...args);
    const end = Date.now();

    console.log('Execution time:', end - start);
    return result;
  };
}

const loggedFormat = withLogging(format);

Такой подход применяется при оптимизации производительности и отладке.


Обертки для работы с локалями

Локализация требует единых точек настройки:

import { format } from 'date-fns';
import { ru } from 'date-fns/locale';

function formatRu(dateInput, formatString) {
  const date = toDate(dateInput);
  if (!date) return null;

  return format(date, formatString, { locale: ru });
}

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


Кэширование результатов форматирования

В высоконагруженных системах форматирование может оптимизироваться через мемоизацию.

import { format } from 'date-fns';

const cache = new Map();

function cachedFormat(dateInput, formatString) {
  const date = toDate(dateInput);
  if (!date) return null;

  const key = `${date.getTime()}_${formatString}`;

  if (cache.has(key)) return cache.get(key);

  const result = format(date, formatString);
  cache.set(key, result);

  return result;
}

Такой подход полезен при массовом рендеринге списков.


Обобщенные фабрики оберток

Масштабируемость достигается через фабрики, создающие специализированные функции:

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

function createDatePipeline({ daysToAdd = 0, formatString }) {
  return function(dateInput) {
    const date = toDate(dateInput);
    if (!date) return null;

    const shifted = addDays(date, daysToAdd);
    return format(shifted, formatString);
  };
}

const formatNextWeek = createDatePipeline({
  daysToAdd: 7,
  formatString: 'yyyy-MM-dd'
});

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


Обертки как слой архитектуры

В архитектурном разрезе обертки над date-fns выполняют роль промежуточного слоя:

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

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