Avatar ·

Как сравнить две даты в JavaScript: полное руководство

Привет, коллеги! Я новичок в JavaScript и столкнулся с проблемой сравнения дат. У меня есть форма, где пользователь выбирает дату начала и дату окончания, и мне нужно проверить, что дата окончания позже даты начала. Вот мой код: ```javascript const startDate = new Date('2023-10-15'); const endDate = new Date('2023-10-20'); if (startDate > endDate) { console.log('Ошибка: дата начала позже даты окончания'); } else { console.log('Всё ок'); } ``` Но я заметил, что иногда сравнение работает неправильно. Например, если я сравниваю только даты без времени, то `new Date('2023-10-15')` и `new Date('2023-10-15')` считаются разными? Или если добавить время, то сравнение ломается. Я погуглил, но ответы разрозненные. Кто-то говорит использовать `getTime()`, кто-то — `valueOf()`, а кто-то — библиотеки типа moment.js. Подскажите, как правильно и надёжно сравнивать даты в чистом JavaScript, чтобы учесть и даты, и время, и временные зоны? Заранее спасибо! <<>>

Почему простое сравнение операторами работает, но не всегда

JavaScript позволяет сравнивать объекты Date операторами >, <, >=, <=. Это работает, потому что при сравнении объекты неявно преобразуются в числа (миллисекунды с 1 января 1970 года). Однако операторы == и === так не работают — они сравнивают ссылки на объекты, а не их значения. Поэтому new Date('2023-10-15') == new Date('2023-10-15') вернёт false, хотя даты одинаковые.

Лучший способ: преобразование в число через getTime()

Самый надёжный метод — использовать getTime(), который возвращает количество миллисекунд с 1 января 1970 года UTC. Это число можно сравнивать любыми операторами, включая ===.

const date1 = new Date('2023-10-15T10:00:00');
const date2 = new Date('2023-10-15T10:00:00');

// Проверка на равенство
if (date1.getTime() === date2.getTime()) {
  console.log('Даты равны');
}

// Проверка на "раньше"
if (date1.getTime() < date2.getTime()) {
  console.log('date1 раньше date2');
}

// Проверка на "позже"
if (date1.getTime() > date2.getTime()) {
  console.log('date1 позже date2');
}

Альтернативные методы: valueOf() и Number()

Метод valueOf() делает то же самое, что и getTime(). Вы также можете использовать унарный плюс:

const date1 = new Date('2023-10-15');
const date2 = new Date('2023-10-20');

// Через valueOf()
if (date1.valueOf() < date2.valueOf()) {
  console.log('date1 раньше');
}

// Через унарный плюс (преобразование в число)
if (+date1 < +date2) {
  console.log('date1 раньше');
}

Проблема с временными зонами

Когда вы создаёте дату из строки в формате '2023-10-15' (только дата), JavaScript интерпретирует её как полночь по UTC. Если вы создаёте дату через new Date(2023, 9, 15) (месяцы с 0), то она будет в локальном часовом поясе. Это может привести к путанице при сравнении. Рекомендуется всегда использовать UTC или явно указывать часовой пояс.

// Создание даты в UTC
const utcDate = new Date(Date.UTC(2023, 9, 15)); // 15 октября 2023, 00:00 UTC

// Лучше избегать строковых форматов, если важна точность
const dateFromParts = new Date(2023, 9, 15); // Локальное время

Сравнение только дат (без времени)

Если нужно сравнить только день, месяц и год, игнорируя время, обнулите часы, минуты, секунды и миллисекунды:

function compareDatesOnly(date1, date2) {
  const d1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate());
  const d2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate());
  
  if (d1.getTime() === d2.getTime()) return 'равны';
  if (d1.getTime() < d2.getTime()) return 'первая раньше';
  return 'первая позже';
}

// Пример
const a = new Date('2023-10-15T23:59:59');
const b = new Date('2023-10-16T00:00:00');
console.log(compareDatesOnly(a, b)); // "первая раньше" — игнорируем время

Итог: универсальная функция сравнения

function compareDates(date1, date2, ignoreTime = false) {
  const d1 = ignoreTime 
    ? new Date(date1.getFullYear(), date1.getMonth(), date1.getDate())
    : new Date(date1);
  const d2 = ignoreTime 
    ? new Date(date2.getFullYear(), date2.getMonth(), date2.getDate())
    : new Date(date2);
    
  const t1 = d1.getTime();
  const t2 = d2.getTime();
  
  if (t1 === t2) return 0;
  return t1 < t2 ? -1 : 1;
}

// Использование:
const start = new Date('2023-10-15');
const end = new Date('2023-10-20');
console.log(compareDates(start, end)); // -1 (start раньше)
console.log(compareDates(end, start)); // 1 (end позже)

Избегайте библиотек, если вам нужно только сравнение — чистый JavaScript справляется отлично. Главное — всегда использовать getTime() или valueOf() для точного сравнения и помнить о временных зонах при создании дат.

<<>>

Сравнение дат через операторы сравнения с явным приведением

Хотя метод getTime() — золотой стандарт, можно использовать и более короткий подход: просто вычесть даты друг из друга. Результат вычитания — разница в миллисекундах, которая может быть отрицательной, нулевой или положительной.

const date1 = new Date('2023-10-15');
const date2 = new Date('2023-10-20');

const diff = date2 - date1; // 432000000 (5 дней в миллисекундах)

if (diff > 0) {
  console.log('date2 позже date1');
} else if (diff < 0) {
  console.log('date2 раньше date1');
} else {
  console.log('даты равны');
}

Этот метод работает, потому что оператор вычитания неявно вызывает valueOf() для обоих операндов. Однако он не подходит для проверки на строгое равенство (нужно дополнительно использовать getTime()).

Когда использовать библиотеки

Если в проекте уже есть moment.js или date-fns, можно воспользоваться их методами:

// date-fns
import { compareAsc, compareDesc, isEqual } from 'date-fns';

const a = new Date('2023-10-15');
const b = new Date('2023-10-20');

console.log(compareAsc(a, b)); // -1 (a раньше)
console.log(isEqual(a, b));    // false

Но для простого сравнения двух дат без дополнительных манипуляций (форматирование, манипуляции с временными зонами) библиотеки избыточны. Чистый JavaScript с getTime() — самый лёгкий и быстрый вариант.