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

В библиотеке js-joda даты и время представлены неизменяемыми (immutable) объектами. Это означает, что любая операция сравнения не модифицирует исходные значения, а возвращает результат в виде нового значения или булевого флага.

Основные типы, участвующие в сравнении:

  • LocalDate — дата без времени и часового пояса
  • LocalDateTime — дата и время без часового пояса
  • ZonedDateTime — дата и время с часовым поясом
  • Instant — момент времени в UTC
  • OffsetDateTime — дата и время с фиксированным смещением

Каждый из этих типов поддерживает единый набор методов сравнения, но их семантика зависит от контекста (особенно при работе с часовыми поясами).


Сравнение через isBefore и isAfter

Наиболее прямолинейные методы сравнения:

  • isBefore(other) — возвращает true, если текущий объект раньше указанного
  • isAfter(other) — возвращает true, если текущий объект позже указанного

LocalDate

import { LocalDate } from '@js-joda/core';

const d1 = LocalDate.parse('2026-01-10');
const d2 = LocalDate.parse('2026-01-20');

d1.isBefore(d2); // true
d2.isAfter(d1);  // true

Сравнение выполняется по календарной логике без учёта времени суток.


Instant

import { Instant } from '@js-joda/core';

const t1 = Instant.parse('2026-01-10T10:00:00Z');
const t2 = Instant.parse('2026-01-10T12:00:00Z');

t1.isBefore(t2); // true

Здесь сравнение происходит по абсолютной временной шкале UTC.


Метод compareTo

Метод compareTo возвращает числовой результат:

  • -1 — текущий объект меньше (раньше)
  • 0 — равны
  • 1 — больше (позже)
const result = d1.compareTo(d2);

if (result < 0) {
  // d1 раньше d2
}

Этот метод особенно полезен при сортировке массивов дат:

const dates = [
  LocalDate.parse('2026-05-01'),
  LocalDate.parse('2026-01-01'),
  LocalDate.parse('2026-03-01')
];

dates.sort((a, b) => a.compareTo(b));

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

Метод equals проверяет полное совпадение значений.

const a = LocalDate.parse('2026-06-01');
const b = LocalDate.parse('2026-06-01');

a.equals(b); // true

Особенности:

  • сравнение строго типизировано
  • разные типы всегда не равны, даже если значения эквивалентны по смыслу
  • учитываются все компоненты (дата, время, зона)

Пример различия типов:

const d = LocalDate.parse('2026-06-01');
const t = Instant.parse('2026-06-01T00:00:00Z');

d.equals(t); // false

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

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

import { ZonedDateTime, ZoneId } from '@js-joda/core';

const z1 = ZonedDateTime.parse('2026-01-10T10:00+03:00[Europe/Moscow]');
const z2 = ZonedDateTime.parse('2026-01-10T07:00Z[UTC]');

Оба объекта представляют один и тот же момент времени, но:

z1.equals(z2);     // false
z1.toInstant().equals(z2.toInstant()); // true

Вывод: для проверки реального момента времени следует использовать toInstant().


Нормализация перед сравнением

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

Приведение к Instant

const i1 = z1.toInstant();
const i2 = z2.toInstant();

i1.isBefore(i2); // корректное сравнение момента времени

Приведение LocalDateTime к зоне

const zdt = localDateTime.atZone(ZoneId.of('Europe/Moscow'));

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

Частая задача — проверка попадания даты в интервал.

const date = LocalDate.parse('2026-05-10');

const start = LocalDate.parse('2026-05-01');
const end = LocalDate.parse('2026-05-31');

const inRange = (date.isAfter(start) || date.equals(start)) &&
                (date.isBefore(end) || date.equals(end));

Более компактная форма:

const inRange = !date.isBefore(start) && !date.isAfter(end);

Сортировка и стабильные сравнения

При сортировке важно использовать один и тот же критерий сравнения:

const events = [
  ZonedDateTime.parse('2026-01-10T10:00+03:00[Europe/Moscow]'),
  ZonedDateTime.parse('2026-01-10T08:00+01:00[Europe/Paris]'),
  ZonedDateTime.parse('2026-01-10T09:00Z[UTC]')
];

events.sort((a, b) => a.toInstant().compareTo(b.toInstant()));

Такой подход исключает ошибки, связанные с локальными зонами.


Сравнение с использованием ChronoUnit

Иногда требуется не просто сравнение, а вычисление разницы.

import { ChronoUnit } from '@js-joda/core';

const days = ChronoUnit.DAYS.between(start, end);

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

if (ChronoUnit.DAYS.between(d1, d2) > 0) {
  // d1 раньше d2
}

Особенности сравнения разных типов

Сравнение между различными временными типами требует осторожности:

  • LocalDate нельзя напрямую сравнивать с Instant
  • LocalDateTime не содержит информации о зоне
  • ZonedDateTime требует нормализации для точного сравнения момента

Типичная ошибка:

LocalDate.parse('2026-01-01').equals(Instant.now()); // всегда false

Корректный подход — приведение к общей модели времени.


Лексикографический порядок и календарная логика

js-joda не использует строковое сравнение. Однако внутренний порядок компонентов соответствует календарной иерархии:

  1. год
  2. месяц
  3. день
  4. время
  5. наносекунды

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


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

Методы библиотеки не предназначены для работы с null. Попытка вызова:

null.isBefore(date);

приведёт к ошибке выполнения. Поэтому сравнение всегда предполагает валидные экземпляры объектов js-joda.


Согласованность операций сравнения

Для избежания логических ошибок важно придерживаться одного подхода:

  • либо использовать isBefore / isAfter
  • либо использовать compareTo
  • либо нормализовать к Instant и сравнивать через него

Смешивание подходов в одном алгоритме приводит к неоднозначным результатам, особенно при наличии временных зон.