Фильтрация массивов дат

Фильтрация массивов дат в JavaScript с использованием date-fns строится на комбинации функций сравнения и предикатов, позволяющих работать с объектами Date на уровне чистых функций без мутаций и побочных эффектов. Библиотека предоставляет набор инструментов для проверки временных интервалов, сравнения дат, нормализации значений и построения выразительных условий отбора элементов массива.

Обработка массивов дат обычно опирается на метод Array.prototype.filter, который принимает предикатную функцию. В связке с date-fns этот предикат формируется из чистых функций сравнения:

  • проверка «раньше/позже»
  • принадлежность интервалу
  • равенство дат
  • сравнение с точностью до дня, месяца, года

Ключевая особенность подхода заключается в отсутствии изменения исходных объектов Date. Все операции выполняются через создание новых значений и булевы проверки.

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

const dates = [
  new Date(2024, 0, 10),
  new Date(2024, 5, 15),
  new Date(2025, 2, 1)
];

const cutoff = new Date(2024, 4, 1);

const result = dates.filter(date => isAfter(date, cutoff));

Функция isAfter возвращает истинное значение только для дат, которые строго позже указанной границы.

Сравнение дат для условий фильтрации

Базовые функции сравнения являются фундаментом отбора элементов.

isBefore и isAfter

Функции isBefore и isAfter используются для построения диапазонных фильтров.

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

const start = new Date(2024, 0, 1);
const end = new Date(2024, 11, 31);

const filtered = dates.filter(date =>
  isAfter(date, start) && isBefore(date, end)
);

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

isEqual и точные совпадения

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

import { isEqual } from 'date-fns';

const target = new Date(2024, 5, 15);

const matches = dates.filter(date =>
  isEqual(date, target)
);

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

Фильтрация по временным интервалам

Наиболее выразительный способ отбора реализуется через isWithinInterval, позволяющую задавать диапазон объектом.

import { isWithinInterval } from 'date-fns';

const range = {
  start: new Date(2024, 0, 1),
  end: new Date(2024, 11, 31)
};

const filtered = dates.filter(date =>
  isWithinInterval(date, range)
);

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

Нормализация дат перед фильтрацией

При работе с реальными данными часто возникает необходимость игнорировать временную составляющую. Date-fns предоставляет функции округления и усечения:

  • startOfDay
  • endOfDay
  • startOfMonth
  • endOfMonth
import { startOfDay, isEqual } from 'date-fns';

const normalizedTarget = startOfDay(new Date(2024, 5, 15));

const filtered = dates.filter(date =>
  isEqual(startOfDay(date), normalizedTarget)
);

Приведение к началу дня устраняет влияние часов, минут и секунд на результат сравнения.

Сложные условия фильтрации

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

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

const start = new Date(2024, 0, 1);
const end = new Date(2024, 11, 31);

const filtered = dates.filter(date =>
  isAfter(date, start) &&
  isBefore(date, end) &&
  !isWeekend(date)
);

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

Группировка и последующая фильтрация

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

import { isWeekend } from 'date-fns';

const workdays = dates.filter(date => !isWeekend(date));

Или выделение дат по месяцам:

import { getMonth } from 'date-fns';

const targetMonth = 5;

const juneDates = dates.filter(date =>
  getMonth(date) === targetMonth
);

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

Использование compareAsc для сортировки перед фильтрацией

В некоторых сценариях предварительная сортировка упрощает последующую обработку. Функция compareAsc обеспечивает упорядочивание дат по возрастанию.

import { compareAsc } from 'date-fns';

const sorted = [...dates].sort(compareAsc);

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

Фильтрация с учётом точности времени

Date-fns позволяет учитывать различную степень точности при сравнении. Например, игнорирование минут и секунд:

import { isSameHour } from 'date-fns';

const filtered = dates.filter(date =>
  isSameHour(date, new Date(2024, 5, 15, 10))
);

Подобные функции включают:

  • isSameDay
  • isSameMonth
  • isSameYear
  • isSameHour

Они формируют уровень абстракции над стандартным сравнением Date.

Интервальные вычисления и генерация диапазонов

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

import { eachDayOfInterval, isWeekend } from 'date-fns';

const interval = {
  start: new Date(2024, 0, 1),
  end: new Date(2024, 0, 31)
};

const days = eachDayOfInterval(interval);

const weekdays = days.filter(date => !isWeekend(date));

Подход объединяет генерацию последовательности и её последующую фильтрацию.

Производительность при работе с большими массивами дат

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

  • минимизация повторных вычислений границ
  • вынесение константных дат за пределы предикатов
  • предварительная нормализация данных
import { isAfter, isBefore } from 'date-fns';

const start = new Date(2024, 0, 1);
const end = new Date(2024, 11, 31);

const filtered = dates.filter(date => {
  return isAfter(date, start) && isBefore(date, end);
});

Вынесение start и end снижает накладные расходы при каждом вызове фильтра.

Составные предикаты и переиспользование логики

Фильтрация дат часто оформляется через переиспользуемые функции-предикаты.

import { isWithinInterval, isWeekend } from 'date-fns';

const isWorkdayInRange = (date, range) =>
  isWithinInterval(date, range) && !isWeekend(date);

const range = {
  start: new Date(2024, 0, 1),
  end: new Date(2024, 11, 31)
};

const result = dates.filter(date =>
  isWorkdayInRange(date, range)
);

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

Фильтрация с учётом временных зон и нормализации входных данных

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

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

const parsed = isoDates.map(parseISO);

const filtered = parsed.filter(date =>
  isAfter(date, new Date(2024, 0, 1))
);

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

Композиция функций для декларативной фильтрации

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

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

const start = new Date(2024, 0, 1);
const end = new Date(2024, 11, 31);

const predicates = [
  date => isAfter(date, start),
  date => isBefore(date, end),
  date => !isWeekend(date)
];

const filtered = dates.filter(date =>
  predicates.every(fn => fn(date))
);

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