Метод equals

Назначение и семантика сравнения

Метод equals в Luxon используется для строгого сравнения двух объектов DateTime. Его задача — определить полное совпадение временных значений с учётом контекста представления даты и времени.

Сравнение выполняется не на уровне строкового отображения и не на уровне отдельных компонентов (год, месяц, день), а на уровне внутреннего представления объекта.

Ключевой принцип:

Два DateTime считаются равными только тогда, когда совпадает их абсолютный момент времени и эквивалентен контекст временной зоны (и связанное с ней представление).


Логика сравнения

При вызове:

dt1.equals(dt2)

происходит проверка следующих аспектов:

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

Если хотя бы один из ключевых параметров отличается, результатом будет false.


Абсолютное время и миллисекунды

Основой сравнения выступает Unix-время в миллисекундах:

const a = DateTime.fromISO("2024-01-01T00:00:00Z");
const b = DateTime.fromMillis(1704067200000);

a.equals(b); // true

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


Влияние временной зоны

Luxon учитывает временную зону как часть контекста объекта. Это означает, что два объекта могут указывать на один и тот же момент времени, но при этом считаться не равными, если их зоны различаются.

const a = DateTime.fromISO("2024-01-01T00:00:00Z");
const b = DateTime.fromISO("2024-01-01T03:00:00+03:00");

a.equals(b); // true (один и тот же момент времени)

В данном случае различие строкового представления компенсируется нормализацией к UTC-времени.

Однако при создании объектов с разными зонами без эквивалентного сдвига ситуация может отличаться:

const a = DateTime.fromObject(
  { year: 2024, month: 1, day: 1, hour: 0 },
  { zone: "utc" }
);

const b = DateTime.fromObject(
  { year: 2024, month: 1, day: 1, hour: 0 },
  { zone: "Europe/Moscow" }
);

a.equals(b); // false

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


Отличие от hasSame

Метод equals часто путают с hasSame, однако их семантика принципиально различна.

  • equals — полное совпадение момента времени
  • hasSame — совпадение по выбранной единице измерения

Пример:

const a = DateTime.fromISO("2024-01-01T10:15:00Z");
const b = DateTime.fromISO("2024-01-01T10:45:00Z");

a.equals(b); // false
a.hasSame(b, "day"); // true

hasSame игнорирует различия внутри указанной гранулярности, тогда как equals требует идентичности всего значения.


Сравнение через toMillis

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

dt1.toMillis() === dt2.toMillis()

Однако такой подход не учитывает внутренние проверки валидности объектов и контекстные аспекты Luxon. Метод equals инкапсулирует эту логику и является предпочтительным способом сравнения.


Поведение с невалидными объектами

Luxon может создавать невалидные DateTime (например, при ошибочном парсинге). В этом случае:

  • equals всегда возвращает false, если хотя бы один объект невалиден
const a = DateTime.invalid("invalid input");
const b = DateTime.now();

a.equals(b); // false

Идентичность и иммутабельность

Объекты DateTime в Luxon являются неизменяемыми. Каждый метод, изменяющий значение (например, plus, set), возвращает новый экземпляр.

Это делает equals безопасным для использования в цепочках преобразований:

const base = DateTime.now();
const modified = base.plus({ days: 1 });

base.equals(modified); // false

При этом оригинальный объект не изменяется, что исключает побочные эффекты сравнения.


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

Проверка кэшированных значений

if (!cachedDate.equals(currentDate)) {
  updateCache();
}

Синхронизация временных меток

const isSameMoment = serverTime.equals(clientTime);

Защита от повторной обработки событий

if (lastProcessed.equals(event.timestamp)) {
  skip();
}

Особенности сравнения с округлёнными значениями

Даже минимальные различия в миллисекундах приводят к неравенству:

const a = DateTime.now();
const b = a.plus({ milliseconds: 1 });

a.equals(b); // false

Для сценариев, где допустима погрешность, требуется предварительное округление или использование hasSame.


Сравнение в разных календарях

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


Типичные ошибки при использовании

  • сравнение объектов без нормализации зоны
  • попытка использовать equals как аналог ===
  • игнорирование миллисекундной точности
  • смешивание DateTime и Date

Взаимодействие с Date

При сравнении с нативным Date необходимо явное преобразование:

const dt = DateTime.now();
const jsDate = new Date();

dt.equals(DateTime.fromJSDate(jsDate));

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