Сравнение дат в JavaScript: как не наступить на грабли и ускорить код
Представьте: вы пишете интернет-магазин, и вам нужно проверить, не истекла ли скидка на товар. Или делаете календарь задач, где нужно отфильтровать события за сегодня. Или собираете аналитику — "покажи все заказы за последнюю неделю". Везде вам нужно сравнить две даты. Казалось бы, что может быть проще? Но JavaScript, как всегда, готов преподнести сюрпризы.
Вопрос "Compare two dates with JavaScript" на Stack Overflow собрал тысячи просмотров — и это не случайно. Разработчики, даже опытные, часто спотыкаются о неочевидные особенности работы с датами. В этой статье мы не просто покажем, как сравнить две даты, но и разберем, какой способ быстрее, а какой — безопаснее. Погнали!
Почему простое сравнение дат не работает?
Начнем с классической ошибки новичка. Допустим, у нас есть две даты:
let date1 = new Date('2024-03-15');
let date2 = new Date('2024-03-20');
console.log(date1 < date2); // true
console.log(date1 > date2); // false
console.log(date1 == date2); // false
С операторами < и > всё работает отлично. JavaScript преобразует объекты Date в числа (миллисекунды) и сравнивает их. Но оператор == (и строгий ===) ведет себя иначе. Он сравнивает ссылки на объекты, а не их значения. Две разные даты — это два разных объекта, поэтому == всегда вернет false, даже если даты совпадают до миллисекунды.
Это первая ловушка. Отсюда мораль: никогда не используйте == или === для сравнения дат. Только <, >, <=, >= или специальные методы.
Метод getTime(): золотой стандарт сравнения дат
Самый надежный и производительный способ — использовать метод getTime(). Он возвращает количество миллисекунд, прошедших с 1 января 1970 года (Unix-эпоха). А числа сравнивать JavaScript умеет отлично:
let date1 = new Date('2024-03-15T10:00:00');
let date2 = new Date('2024-03-15T10:00:00');
let date3 = new Date('2024-03-20T10:00:00');
console.log(date1.getTime() === date2.getTime()); // true
console.log(date1.getTime() < date3.getTime()); // true
console.log(date1.getTime() > date3.getTime()); // false
Почему это эффективно? Потому что getTime() — это очень легковесная операция. Она просто возвращает уже хранящееся внутри объекта Date числовое значение. Никакого парсинга, никаких дополнительных вычислений. Просто число.
Давайте замерим производительность. Вот небольшой тест в консоли браузера:
let date1 = new Date('2024-01-01');
let date2 = new Date('2024-12-31');
console.time('getTime');
for (let i = 0; i < 1000000; i++) {
date1.getTime() < date2.getTime();
}
console.timeEnd('getTime');
// Результат: ~5-10 ms в современных браузерах
Миллион сравнений за 10 миллисекунд — это отличный результат. Для сравнения, альтернативные методы могут быть в 2-3 раза медленнее.
Сравнение дат с помощью valueOf()
Метод valueOf() делает то же самое, что и getTime() — возвращает числовое представление даты. Но есть нюанс: при использовании операторов сравнения (<, > и т.д.) JavaScript неявно вызывает valueOf() для объектов Date. То есть вот этот код:
let date1 = new Date('2024-03-15');
let date2 = new Date('2024-03-20');
console.log(date1 < date2);
На самом деле работает как:
console.log(date1.valueOf() < date2.valueOf());
Поэтому для простых сравнений < и > можно использовать напрямую объекты Date. Но для точного равенства (===) это не сработает, и тут без getTime() или valueOf() не обойтись.
Лично я рекомендую всегда явно использовать getTime() — это делает код более читаемым и явным. Вы сразу видите, что сравниваются числа, а не объекты.
Оператор сравнения Date.parse(): когда он нужен?
Метод Date.parse() тоже может участвовать в сравнении дат, но с оговорками. Он принимает строку и возвращает количество миллисекунд:
let timestamp1 = Date.parse('2024-03-15T10:00:00Z');
let timestamp2 = Date.parse('2024-03-20T10:00:00Z');
console.log(timestamp1 < timestamp2); // true
Казалось бы, удобно — не нужно создавать объекты Date. Но есть подвох: производительность. Date.parse() каждый раз парсит строку, а это довольно тяжелая операция. Если вы делаете это в цикле или часто — производительность упадет.
Сравним:
console.time('Date.parse');
for (let i = 0; i < 1000000; i++) {
Date.parse('2024-03-15') < Date.parse('2024-03-20');
}
console.timeEnd('Date.parse');
// Результат: ~150-200 ms
console.time('getTime with Date objects');
let d1 = new Date('2024-03-15');
let d2 = new Date('2024-03-20');
for (let i = 0; i < 1000000; i++) {
d1.getTime() < d2.getTime();
}
console.timeEnd('getTime with Date objects');
// Результат: ~5-10 ms
Разница в 20-30 раз! Мораль: не используйте Date.parse() в циклах или при многократном сравнении. Создайте объекты Date один раз и используйте getTime().
Сравнение дат без времени: как отбросить часы и минуты
Часто нужно сравнить только даты, игнорируя время. Например, проверить, является ли сегодняшний день дедлайном. Простое сравнение объектов Date не подойдет, потому что new Date() содержит текущее время вплоть до миллисекунд.
Решение — обнулить время. Самый элегантный способ:
function compareDatesOnly(date1, date2) {
// Обрезаем время, оставляем только год-месяц-день
let d1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate());
let d2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate());
return d1.getTime() === d2.getTime();
}
let today = new Date(); // 2024-03-15T14:30:00
let deadline = new Date('2024-03-15