Сравнение дат

Библиотека date-fns предоставляет набор функций для работы с датами в неизменяемом (immutable) стиле, где каждая операция возвращает новый результат без модификации исходных данных. Сравнение дат — одна из базовых задач, на которой строится логика фильтрации, сортировки, валидации временных интервалов и построения бизнес-правил.

Базовое представление дат

Все функции сравнения в date-fns принимают значения, которые приводятся к типу Date. Допустимые входные форматы:

  • объект Date
  • timestamp (число миллисекунд)
  • строка, распознаваемая конструктором Date

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


Проверка равенства дат

isEqual

Функция isEqual проверяет абсолютное совпадение двух дат вплоть до миллисекунд.

import { isEqual } from 'date-fns';

isEqual(new Date(2024, 0, 1), new Date(2024, 0, 1)); // true
isEqual(new Date(2024, 0, 1, 10), new Date(2024, 0, 1, 11)); // false

Особенность поведения:

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

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


Хронологическое сравнение

isBefore

Функция isBefore определяет, предшествует ли первая дата второй.

import { isBefore } from 'date-fns';

isBefore(new Date(2023, 0, 1), new Date(2024, 0, 1)); // true
isBefore(new Date(2024, 0, 2), new Date(2024, 0, 1)); // false

Логика:

  • сравнение выполняется по timestamp
  • возвращает true, если первая дата строго меньше второй
  • равенство не считается «до»

isAfter

Обратная операция — проверка, следует ли первая дата после второй.

import { isAfter } from 'date-fns';

isAfter(new Date(2024, 0, 2), new Date(2024, 0, 1)); // true
isAfter(new Date(2024, 0, 1), new Date(2024, 0, 1)); // false

Используется в сценариях:

  • контроль дедлайнов
  • фильтрация актуальных событий
  • проверка актуальности данных

Трёхстороннее сравнение

compareAsc

Функция compareAsc возвращает числовой результат сравнения:

  • -1 — первая дата раньше второй
  • 1 — первая дата позже второй
  • 0 — даты равны
import { compareAsc } from 'date-fns';

compareAsc(new Date(2023, 0, 1), new Date(2024, 0, 1)); // -1
compareAsc(new Date(2024, 0, 2), new Date(2024, 0, 1)); // 1
compareAsc(new Date(2024, 0, 1), new Date(2024, 0, 1)); // 0

Основное применение — сортировка массивов дат:

dates.sort(compareAsc);

compareDesc

Обратный порядок сортировки:

import { compareDesc } from 'date-fns';

dates.sort(compareDesc);

Поведение:

  • 1 если первая дата позже второй
  • -1 если раньше
  • 0 при равенстве

Используется для:

  • построения лент событий (новые сверху)
  • журналов логирования
  • аналитических отчетов

Сравнение по диапазонам времени

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

Проверка принадлежности интервалу

const isInRange = (date, start, end) =>
  isAfter(date, start) && isBefore(date, end);

Такой подход используется при:

  • проверке бронирований
  • фильтрации событий по периоду
  • ограничении доступа по времени

Особенности работы с временем суток

Сравнение дат в date-fns всегда учитывает время, что приводит к важным нюансам:

new Date(2024, 0, 1, 0, 0)   // начало дня
new Date(2024, 0, 1, 23, 59) // конец дня

Эти значения не равны, несмотря на одинаковую календарную дату.

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

  • isSameDay
  • startOfDay
  • endOfDay

Пример нормализации:

import { startOfDay, isEqual } from 'date-fns';

isEqual(
  startOfDay(new Date(2024, 0, 1, 10)),
  startOfDay(new Date(2024, 0, 1, 22))
); // true

Сравнение с учётом временных зон

date-fns оперирует объектами Date в локальной временной зоне среды выполнения. Это означает:

  • сравнение происходит по абсолютному времени (UTC timestamp)
  • отображение зависит от локальной зоны
  • различия возникают только на уровне интерпретации, а не вычислений

Пример:

isBefore('2024-01-01T00:00:00Z', '2024-01-01T03:00:00+03:00'); // false или true зависит от UTC

Фактически оба значения приводятся к одному моменту времени.


Типовые сценарии использования сравнения

Сортировка событий

import { compareDesc } from 'date-fns';

const events = [
  { date: new Date(2024, 5, 1) },
  { date: new Date(2023, 5, 1) },
];

events.sort((a, b) => compareDesc(a.date, b.date));

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

import { isBefore } from 'date-fns';

const now = new Date();

const expired = items.filter(item =>
  isBefore(item.expirationDate, now)
);

Проверка актуальности данных

import { isAfter } from 'date-fns';

const isFresh = (updatedAt) =>
  isAfter(updatedAt, new Date(Date.now() - 1000 * 60 * 60));

Различие между строгим и логическим сравнением

Сравнение в date-fns делится на два уровня:

  1. Абсолютное сравнение

    • isEqual
    • учитывает миллисекунды
  2. Относительное сравнение

    • isBefore
    • isAfter
    • compareAsc
    • compareDesc

Абсолютное сравнение отвечает за идентичность, относительное — за порядок.


Ошибки при сравнении дат

Сравнение строк вместо Date

isBefore('2024-01-01', '2024-02-01');

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


Игнорирование времени

isEqual(new Date(2024, 0, 1), new Date(2024, 0, 1, 12)); // false

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


Неправильная сортировка без comparator

dates.sort(); // некорректно для Date объектов

Без compareAsc сортировка выполняется как строковая, что нарушает хронологию.


Комбинирование функций сравнения

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

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

const compareStatus = (a, b) => {
  if (isEqual(a, b)) return 'same';
  if (isBefore(a, b)) return 'before';
  if (isAfter(a, b)) return 'after';
};

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