Работа в Angular

Библиотека date-fns применяется в Angular-проектах как легковесная альтернатива встроенному DatePipe и более тяжёлым решениям вроде Moment.js. Основная идея интеграции заключается в использовании чистых функций для работы с датами внутри сервисов, пайпов и бизнес-логики, сохраняя при этом предсказуемость и удобство тестирования.

Ключевая особенность подхода — отсутствие мутаций и модульная структура: каждая функция импортируется отдельно, что позволяет эффективно контролировать размер бандла.

Установка и базовая настройка

Подключение библиотеки выполняется стандартным образом через пакетный менеджер:

npm install date-fns

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

npm install date-fns

Локали в date-fns поставляются вместе с основным пакетом и импортируются точечно.

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

import { format } from 'date-fns';

const result = format(new Date(), 'yyyy-MM-dd');

Использование в Angular-сервисах

В Angular архитектуре предпочтительно выносить работу с датами в отдельный сервис, инкапсулирующий вызовы date-fns. Это упрощает поддержку и повышает тестируемость.

import { Injectable } from '@angular/core';
import { format, parseISO, differenceInDays } from 'date-fns';

@Injectable({
  providedIn: 'root'
})
export class DateService {

  formatDate(date: string | Date): string {
    const parsed = typeof date === 'string' ? parseISO(date) : date;
    return format(parsed, 'dd.MM.yyyy');
  }

  getDaysBetween(start: Date, end: Date): number {
    return differenceInDays(end, start);
  }
}

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

Создание Angular Pipe на основе date-fns

Пайпы позволяют интегрировать date-fns прямо в шаблоны Angular.

import { Pipe, PipeTransform } from '@angular/core';
import { format } from 'date-fns';

@Pipe({
  name: 'dfnsDate'
})
export class DateFnsPipe implements PipeTransform {

  transform(value: Date | string, formatStr: string = 'dd.MM.yyyy'): string {
    const date = typeof value === 'string' ? new Date(value) : value;
    return format(date, formatStr);
  }
}

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

<div>
  {{ createdAt | dfnsDate:'yyyy-MM-dd HH:mm' }}
</div>

Подобный пайп делает шаблоны компактнее, но при этом сохраняет гибкость форматирования.

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

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

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

format(new Date(), 'PPP', { locale: ru });

Интеграция с Angular i18n обычно строится через сервис, который выбирает локаль на основе текущего языка приложения.

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

const localeMap = {
  en: enUS,
  ru: ru
};

Стратегия работы с Angular Change Detection

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

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

Рекомендуемая практика — перенос вычислений в:

  • сервисы
  • мемоизированные свойства
  • пайпы с pure: true
@Pipe({
  name: 'dfnsDate',
  pure: true
})

Производительность и tree-shaking

Одно из ключевых преимуществ date-fns в Angular — поддержка tree-shaking. Импортируются только используемые функции:

import { format } from 'date-fns';

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

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

Работа с Angular Universal (SSR)

В серверном рендеринге Angular важно учитывать отсутствие глобальных браузерных API. date-fns не использует window или document, поэтому совместимость с SSR обеспечивается по умолчанию.

Особенности:

  • даты вычисляются на сервере идентично клиенту
  • возможны расхождения при работе с локальными часовыми поясами
  • рекомендуется фиксировать timezone на уровне логики приложения
format(new Date(), 'yyyy-MM-dd HH:mm', { locale: ru });

При необходимости синхронизации времени между сервером и клиентом используется передача ISO-строк.

Работа с формами Angular

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

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

const date = parseISO(control.value);

if (isValid(date)) {
  // логика обработки
}

Также удобно выполнять валидацию диапазонов:

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

const valid = isAfter(endDate, startDate);

Сравнение с Angular DatePipe

Angular предоставляет встроенный DatePipe, однако подходы различаются.

DatePipe:

  • работает через Angular DI
  • зависит от локалей Angular
  • используется в шаблонах напрямую

date-fns:

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

Пример различия:

<!-- Angular -->
{{ dateValue | date:'dd.MM.yyyy' }}
// date-fns
format(dateValue, 'dd.MM.yyyy');

date-fns предпочтителен в бизнес-логике и сервисах, тогда как DatePipe удобен для простого отображения в шаблонах.

Композиция функций и утилитарные сценарии

date-fns хорошо подходит для построения цепочек преобразований:

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

const result = format(
  addDays(subMonths(new Date(), 1), 10),
  'yyyy-MM-dd'
);

Такой стиль делает логику дат декларативной и легко тестируемой.

Тестирование логики дат

Чистые функции date-fns упрощают модульное тестирование. Нет необходимости мокать глобальные зависимости.

import { differenceInDays } from 'date-fns';

test('difference in days', () => {
  expect(differenceInDays(
    new Date('2024-01-10'),
    new Date('2024-01-01')
  )).toBe(9);
});

Типичные ошибки интеграции

Частые проблемы при использовании date-fns в Angular:

  • форматирование дат прямо в шаблоне без пайпов
  • повторные вычисления в геттерах компонентов
  • отсутствие нормализации входных строковых дат
  • смешивание DatePipe и date-fns в одной логике
  • игнорирование локалей при мультиязычности

Рациональная архитектура предполагает единый слой работы с датами, отделённый от UI-компонентов.