В JavaScript даты представляют собой момент времени вплоть до
миллисекунд, поэтому любое сравнение без учета нормализации приводит к
различным результатам даже при визуально одинаковых календарных
значениях. В библиотеке Date-fns логика сравнения строится вокруг того,
что дата включает как календарную часть, так и временную, а значит два
объекта Date, относящиеся к одному дню, но разным часам,
считаются различными.
Фундаментальные функции сравнения:
isBefore(date, dateToCompare)isAfter(date, dateToCompare)isEqual(dateLeft, dateRight)compareAsc(dateLeft, dateRight)compareDesc(dateLeft, dateRight)Каждая из них работает с абсолютным временем в миллисекундах, не игнорируя компоненты времени.
Основой всех операций служит преобразование даты в числовое значение:
const a = new Date(2026, 0, 1, 10, 0);
const b = new Date(2026, 0, 1, 12, 0);
isBefore(a, b); // true
isAfter(a, b); // false
isEqual(a, b); // false
Даже при совпадении календарного дня различие во времени полностью меняет результат сравнения.
Функция compareAsc возвращает:
-1, если первая дата меньше1, если больше0, если равныcompareAsc(a, b); // -1
compareAsc(b, a); // 1
compareDesc инвертирует порядок сортировки.
Во многих прикладных задачах время не имеет значения: расписания, календарные события, логика дедлайнов, группировка по дням. В таких случаях прямое сравнение приводит к ошибкам, поскольку учитываются часы, минуты и секунды.
Date-fns не скрывает временную составляющую, поэтому для игнорирования времени используется нормализация даты.
Основные подходы:
startOfDay(date)endOfDay(date)differenceInCalendarDays(dateLeft, dateRight)Наиболее распространенный способ — приведение даты к началу суток:
import { startOfDay, isEqual } from 'date-fns';
const a = startOfDay(new Date(2026, 0, 1, 10, 30));
const b = startOfDay(new Date(2026, 0, 1, 23, 59));
isEqual(a, b); // true
После нормализации обе даты представляют одно и то же мгновение — 00:00:00.
Иногда требуется обратная стратегия — приведение к последнему моменту суток:
import { endOfDay, isEqual } from 'date-fns';
const a = endOfDay(new Date(2026, 0, 1, 1, 0));
const b = endOfDay(new Date(2026, 0, 1, 23, 0));
isEqual(a, b); // true
Такая нормализация используется в фильтрации диапазонов, где важно включить весь день целиком.
Для задач, где важно игнорировать время, но не выполнять ручную нормализацию, применяются функции календарного сравнения.
import { differenceInCalendarDays } from 'date-fns';
const a = new Date(2026, 0, 1, 23, 59);
const b = new Date(2026, 0, 2, 0, 1);
differenceInCalendarDays(b, a); // 1
Функция игнорирует временную составляющую и работает с календарными границами.
Календарные функции опираются на локальные границы дня. Это означает:
| Подход | Учитывает время | Поведение | Применение |
|---|---|---|---|
isBefore / isAfter |
да | точное сравнение по миллисекундам | таймеры, события, логи |
compareAsc / compareDesc |
да | сортировка по времени | списки событий |
startOfDay + isEqual |
нет | нормализация к 00:00 | сравнение дней |
differenceInCalendarDays |
нет | разница по датам | календарные расчеты |
const a = new Date(2026, 0, 1, 0, 0);
const b = new Date(2026, 0, 1, 12, 0);
isEqual(a, b); // false
Обе даты относятся к одному дню, но различие во времени делает их неравными.
===
для объектов Dateconst a = new Date(2026, 0, 1);
const b = new Date(2026, 0, 1);
a === b; // false
Сравнение объектов происходит по ссылке, а не по значению.
Date-fns оперирует локальным временем среды выполнения. При работе с UTC-данными требуется явное приведение:
import { parseISO } from 'date-fns';
const a = parseISO('2026-01-01T00:00:00Z');
const b = new Date(2026, 0, 1);
isEqual(a, b); // зависит от локального часового пояса
В задачах фильтрации часто требуется определить принадлежность даты диапазону:
import { isAfter, isBefore } from 'date-fns';
const inRange = (date, start, end) =>
isAfter(date, start) && isBefore(date, end);
Такой подход учитывает точное время и исключает граничные значения.
Для включающего диапазона используются модификации:
import { isEqual } from 'date-fns';
const inRangeInclusive = (date, start, end) =>
(isAfter(date, start) || isEqual(date, start)) &&
(isBefore(date, end) || isEqual(date, end));
При работе с датами без времени используется нормализация:
import { startOfDay, endOfDay, isWithinInterval } from 'date-fns';
const rangeStart = startOfDay(start);
const rangeEnd = endOfDay(end);
isWithinInterval(date, { start: rangeStart, end: rangeEnd });
Функция isWithinInterval опирается на абсолютные
значения, поэтому предварительная нормализация гарантирует игнорирование
времени.
import { compareAsc } from 'date-fns';
dates.sort(compareAsc);
Результат учитывает миллисекунды, секунды, минуты и часы.
import { startOfDay, compareAsc } from 'date-fns';
dates
.map(startOfDay)
.sort(compareAsc);
После приведения к началу дня различия во времени исчезают, и сортировка становится календарной.
Границы суток являются ключевым источником расхождений при сравнении:
С точки зрения абсолютного времени разница минимальна, но с точки зрения календаря это разные дни.
import { differenceInCalendarDays } from 'date-fns';
differenceInCalendarDays(
new Date(2026, 0, 2, 0, 0),
new Date(2026, 0, 1, 23, 59)
); // 1
isBefore, isAfter, isEqualcompareAscstartOfDay +
isEqual или differenceInCalendarDaysisWithinInterval с
нормализацией границРазделение этих подходов устраняет неоднозначности между календарной и абсолютной моделью времени и позволяет избежать логических ошибок при обработке дат в прикладных сценариях.