Проверка пересечений

В задачах работы с датами и временем одной из ключевых операций становится определение пересечения интервалов. В контексте js-joda это проявляется при анализе диапазонов дат, проверке конфликтов расписаний, объединении событий и построении временных выборок.

Основная сложность заключается в том, что разные типы временных сущностей (LocalDate, LocalDateTime, Instant) требуют разных подходов к сравнению, а также в необходимости учитывать границы интервалов: включительные они или исключительные.


Базовые понятия пересечения

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

Для интервалов:

  • A = [aStart, aEnd)
  • B = [bStart, bEnd)

пересечение существует, если:

  • aStart < bEnd и bStart < aEnd

Это универсальное правило применимо ко всем линейным временным шкалам.


Интервалы через js-joda-extra: Interval

В экосистеме js-joda работа с полноценными временными интервалами реализуется через пакет js-joda-extra, где представлен класс Interval.

Интервал строится на основе Instant:

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

const startA = Instant.parse('2026-01-01T10:00:00Z');
const endA   = Instant.parse('2026-01-01T12:00:00Z');

const startB = Instant.parse('2026-01-01T11:00:00Z');
const endB   = Instant.parse('2026-01-01T13:00:00Z');

const intervalA = Interval.of(startA, endA);
const intervalB = Interval.of(startB, endB);

Проверка пересечения через overlaps

Класс Interval предоставляет метод overlaps, который определяет наличие пересечения между двумя интервалами.

const result = intervalA.overlaps(intervalB);

Результат:

  • true — интервалы пересекаются
  • false — пересечения нет

Логика метода основана на проверке границ интервалов без необходимости ручного сравнения.


Проверка вложенности и касания

Пересечение интервалов часто путается с другими отношениями:

Полное включение

Интервал A включает B, если:

  • A начинается раньше или одновременно с B
  • A заканчивается позже или одновременно с B
intervalA.contains(intervalB);

Касание границ

Интервалы могут не пересекаться, но соприкасаться:

  • конец A == начало B
intervalA.abuts(intervalB);

Такие интервалы не считаются пересекающимися, несмотря на отсутствие промежутка между ними.


Пересечение как конкретный интервал

Если пересечение существует, его можно вычислить:

const intersection = intervalA.intersection(intervalB);

Результатом будет новый Interval, соответствующий общей части диапазонов.

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

if (intervalA.overlaps(intervalB)) {
  const intersection = intervalA.intersection(intervalB);
}

Алгоритмическая модель пересечения

Формально пересечение интервалов A и B определяется через границы:

  • start = max(aStart, bStart)
  • end = min(aEnd, bEnd)

Если start < end, пересечение существует.

const start = aStart.isAfter(bStart) ? aStart : bStart;
const end = aEnd.isBefore(bEnd) ? aEnd : bEnd;

const hasIntersection = start.isBefore(end);

Эта модель полезна при работе без Interval, например при использовании только LocalDateTime.


Пересечения на уровне LocalDate

LocalDate не содержит встроенного класса интервалов, поэтому проверка выполняется вручную.

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

const startA = LocalDate.parse('2026-01-01');
const endA   = LocalDate.parse('2026-01-10');

const startB = LocalDate.parse('2026-01-05');
const endB   = LocalDate.parse('2026-01-15');

Условие пересечения:

const overlaps =
  (startA.isBefore(endB) || startA.equals(endB)) &&
  (startB.isBefore(endA) || startB.equals(endA));

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


Различие пересечений для дат и времени

Instant / UTC-время

  • высокая точность до наносекунд
  • интервалы строго линейны
  • используется Interval

LocalDateTime

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

LocalDate

  • дискретная шкала (дни)
  • пересечение определяется целыми сутками

Пересечения множественных интервалов

При работе с массивами интервалов задача сводится к поиску всех пересекающихся пар.

const intervals = [intervalA, intervalB, intervalC];

for (let i = 0; i < intervals.length; i++) {
  for (let j = i + 1; j < intervals.length; j++) {
    if (intervals[i].overlaps(intervals[j])) {
      // обработка пересечения
    }
  }
}

Сложность такого подхода — O(n²), что становится критичным при больших наборах данных.


Оптимизация через сортировку

Упорядочивание интервалов по началу позволяет сократить количество проверок:

const sorted = intervals.sort((a, b) =>
  a.start().compareTo(b.start())
);

for (let i = 0; i < sorted.length; i++) {
  for (let j = i + 1; j < sorted.length; j++) {
    if (!sorted[i].overlaps(sorted[j])) {
      break;
    }
  }
}

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


Пересечения и временные зоны

При использовании ZonedDateTime пересечения могут изменяться при смене временной зоны.

Приведение к Instant устраняет неоднозначность:

  • локальное время → момент времени
  • сравнение → строго линейное
const instantA = zonedDateTimeA.toInstant();
const instantB = zonedDateTimeB.toInstant();

const intervalA = Interval.of(instantA, instantA.plusSeconds(3600));
const intervalB = Interval.of(instantB, instantB.plusSeconds(3600));

Граничные случаи

Нулевой интервал

Если начало и конец совпадают:

  • интервал пустой
  • пересечения отсутствуют

Инвертированные границы

Если start > end:

  • интервал некорректен
  • требуется нормализация данных

Касание без пересечения

  • A.end == B.start
  • overlaps возвращает false
  • abuts возвращает true

Практическая модель выбора проверки

  • overlaps — наличие общей области
  • contains — вложенность интервала
  • ручное сравнение — работа без Interval
  • intersection — получение пересечения
  • abuts — проверка соприкосновения границ

Эти операции формируют базовый набор для анализа временных диапазонов в js-joda и позволяют строить сложные системы планирования и синхронизации данных.