Сравнение и сортировка

В js-joda модель сравнения построена вокруг строгих правил неизменяемых (immutable) временных типов и явной семантики хронологии. Каждый тип времени — Instant, LocalDate, LocalDateTime, ZonedDateTime — имеет собственную систему сравнения, и корректная сортировка требует понимания их различий, а не только применения стандартных операторов JavaScript.

Базовая идея заключается в том, что временные объекты не приводятся автоматически к единому формату. Вместо этого используется явное сравнение через методы:

  • compareTo
  • isBefore
  • isAfter
  • isEqual

compareTo как основной механизм

Все основные типы реализуют метод compareTo, возвращающий:

  • отрицательное значение — объект меньше другого
  • 0 — равенство
  • положительное значение — объект больше другого
import { LocalDate } from '@js-joda/core';

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

d1.compareTo(d2); // < 0
d2.compareTo(d1); // > 0

Семантика сравнения строго типизирована: сравнение возможно только между объектами одного типа. Попытка сравнить LocalDate и LocalDateTime приводит к ошибочной логике или исключению в зависимости от контекста использования.

isBefore, isAfter и isEqual

Эти методы обеспечивают более читаемую форму выражения порядка:

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

В отличие от compareTo, данные методы выражают намерение без необходимости интерпретировать числовой результат.

Сравнение различных временных типов

LocalDate

LocalDate сравнивается исключительно по календарной дате:

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

const a = LocalDate.of(2024, 5, 10);
const b = LocalDate.of(2024, 5, 11);

a.compareTo(b);

Время суток и часовой пояс отсутствуют, поэтому сравнение детерминировано и не зависит от окружения.

LocalDateTime

LocalDateTime включает дату и время, но без часового пояса:

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

const a = LocalDateTime.parse('2024-05-10T10:00');
const b = LocalDateTime.parse('2024-05-10T12:00');

a.isBefore(b);

Сравнение выполняется лексикографически: сначала дата, затем время.

ZonedDateTime и влияние зоны

ZonedDateTime учитывает часовой пояс, что делает сравнение абсолютным во времени.

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

const a = ZonedDateTime.parse('2024-05-10T10:00+03:00[Europe/Moscow]');
const b = ZonedDateTime.parse('2024-05-10T09:00+01:00[Europe/Paris]');

a.isEqual(b); // может быть true в абсолютном времени

В отличие от LocalDateTime, сравнение здесь основано на точке на временной оси, а не на локальном представлении.

Instant

Instant представляет момент времени в UTC и является наиболее однозначным типом для сравнения:

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

const a = Instant.parse('2024-05-10T07:00:00Z');
const b = Instant.parse('2024-05-10T08:00:00Z');

a.isBefore(b);

Сортировка массивов временных объектов

Стандартный подход в JavaScript — использование Array.prototype.sort, но с явным компаратором.

Сортировка LocalDate

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

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

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

Компаратор напрямую делегирует логику библиотеке, что исключает ошибки преобразования.

Обратная сортировка

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

или через инверсию:

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

Сортировка LocalDateTime

При работе с датой и временем сортировка учитывает оба компонента:

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

const items = [
  LocalDateTime.parse('2024-05-10T09:00'),
  LocalDateTime.parse('2024-05-10T08:30'),
  LocalDateTime.parse('2024-05-09T23:00')
];

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

Лексикографический порядок гарантирует корректную сортировку без дополнительных преобразований.

Сортировка ZonedDateTime

При наличии временных зон сортировка становится глобальной:

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

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

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

Порядок определяется моментом времени на UTC-линии, а не локальными представлениями.

Сортировка по нескольким критериям

Часто требуется комбинировать несколько уровней сортировки, например дату и время создания события.

events.sort((a, b) => {
  const dateCompare = a.date.compareTo(b.date);
  if (dateCompare !== 0) return dateCompare;

  return a.time.compareTo(b.time);
});

Такой подход обеспечивает стабильную сортировку при совпадении первичного ключа.

Использование Instant как универсального ключа сортировки

При необходимости унифицировать различные типы времени применяется преобразование к Instant:

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

Это устраняет различия между зонами и локальными представлениями, приводя всё к единой временной оси.

Разность времени как вспомогательный инструмент сортировки

Иногда сортировка строится не напрямую через сравнение, а через вычисление разницы:

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

events.sort((a, b) =>
  ChronoUnit.MINUTES.between(a, b)
);

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

Особенности сортировки и неизменяемость объектов

Все типы в библиотеке являются immutable. Это означает, что:

  • операции сравнения не изменяют объект
  • сортировка не влияет на исходные экземпляры
  • каждый вызов sort работает только с ссылками массива
const original = [date1, date2];
const sorted = [...original].sort((a, b) => a.compareTo(b));

Копирование массива сохраняет исходный порядок данных, что критично для предсказуемости логики.

Сравнение с JavaScript Date

Встроенный Date в JavaScript использует числовое представление времени, что позволяет сравнение через вычитание. В отличие от этого подхода, js-joda требует явного вызова методов, исключая неявные преобразования и ошибки, связанные с часовыми поясами и локалями.

// JS Date
a - b;

// js-joda
a.compareTo(b);

Разделение типов и явные методы сравнения устраняют неоднозначность в интерпретации времени.

Стабильность сортировки и предсказуемость

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

  • строгой типизации временных объектов
  • отсутствию скрытых преобразований
  • детерминированной реализации compareTo

Такая модель особенно важна при обработке временных рядов, журналов событий и расписаний, где порядок должен оставаться неизменным при переносе между системами.