Пересечение интервалов: areIntervalsOverlapping

Функция areIntervalsOverlapping из библиотеки date-fns предназначена для проверки пересечения двух временных интервалов. Временные интервалы в данном контексте представляются объектами с полями start и end, где каждое значение является экземпляром Date или числом (timestamp). Основная задача функции — определить, существует ли хотя бы одна точка времени, общая для обоих интервалов.

Интервалы задаются в виде объектов:

{
  start: Date | number,
  end: Date | number
}

Ключевое требование — корректная временная последовательность внутри одного интервала. То есть start должен быть меньше либо равен end. Хотя библиотека не всегда жёстко валидирует порядок, логическая корректность входных данных напрямую влияет на результат.

Сигнатура функции

areIntervalsOverlapping(
  intervalLeft,
  intervalRight,
  options?
)

Где:

  • intervalLeft — первый интервал
  • intervalRight — второй интервал
  • options — необязательный объект конфигурации

Логика пересечения интервалов

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

  • интервал A и интервал B пересекаются, если начало одного интервала меньше или равно концу другого, и конец одного больше или равен началу другого (в зависимости от настроек включительности границ)

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

Базовое поведение

Пример пересекающихся интервалов:

import { areIntervalsOverlapping } from 'date-fns';

const intervalA = {
  start: new Date(2024, 0, 1),
  end: new Date(2024, 0, 10)
};

const intervalB = {
  start: new Date(2024, 0, 5),
  end: new Date(2024, 0, 15)
};

areIntervalsOverlapping(intervalA, intervalB);
// true

Здесь пересечение существует в диапазоне с 5 по 10 января.

Интервалы без пересечения

const intervalA = {
  start: new Date(2024, 0, 1),
  end: new Date(2024, 0, 5)
};

const intervalB = {
  start: new Date(2024, 0, 6),
  end: new Date(2024, 0, 10)
};

areIntervalsOverlapping(intervalA, intervalB);
// false

Между интервалами существует разрыв, поэтому пересечения нет.

Касание границ интервалов

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

const intervalA = {
  start: new Date(2024, 0, 1),
  end: new Date(2024, 0, 5)
};

const intervalB = {
  start: new Date(2024, 0, 5),
  end: new Date(2024, 0, 10)
};

Результат зависит от параметра inclusive.

Параметр inclusive

Опции функции позволяют управлять тем, считается ли совпадение границ пересечением.

areIntervalsOverlapping(intervalA, intervalB, { inclusive: true });

При inclusive: true границы интервалов считаются частью интервала. В этом случае касание концом одного интервала начала другого будет считаться пересечением.

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

Пример поведения inclusive

inclusive: true

areIntervalsOverlapping(
  {
    start: new Date(2024, 0, 1),
    end: new Date(2024, 0, 5)
  },
  {
    start: new Date(2024, 0, 5),
    end: new Date(2024, 0, 10)
  },
  { inclusive: true }
);
// true

inclusive: false

areIntervalsOverlapping(
  {
    start: new Date(2024, 0, 1),
    end: new Date(2024, 0, 5)
  },
  {
    start: new Date(2024, 0, 5),
    end: new Date(2024, 0, 10)
  },
  { inclusive: false }
);
// false

Математическая модель

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

Пусть:

  • A = [a_start, a_end]
  • B = [b_start, b_end]

Тогда пересечение существует, если:

  • a_start ≤ b_end
  • b_start ≤ a_end

При исключающем режиме используется строгая версия:

  • a_start < b_end
  • b_start < a_end

Практическое применение

Проверка занятости времени

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

const booking = {
  start: new Date(2024, 5, 1, 10),
  end: new Date(2024, 5, 1, 12)
};

const existing = [
  {
    start: new Date(2024, 5, 1, 9),
    end: new Date(2024, 5, 1, 11)
  },
  {
    start: new Date(2024, 5, 1, 12),
    end: new Date(2024, 5, 1, 13)
  }
];

existing.filter(item =>
  areIntervalsOverlapping(booking, item)
);

Результат — обнаружение первого конфликта.

Работа с календарями

При визуализации событий календаря важно группировать пересекающиеся события для корректного отображения слоёв и колонок. areIntervalsOverlapping используется как базовый строительный блок для алгоритмов layout’а календаря.

Обработка нестандартных случаев

Обратные интервалы

Если start больше end, поведение становится непредсказуемым с точки зрения логики приложения. Библиотека не всегда нормализует такие данные автоматически.

Корректный подход — предварительная нормализация:

const normalize = (interval) => ({
  start: interval.start < interval.end ? interval.start : interval.end,
  end: interval.start < interval.end ? interval.end : interval.start
});

Нулевые интервалы

Интервал с одинаковыми границами:

{
  start: date,
  end: date
}

При inclusive: true такой интервал может считаться пересекающимся с другими, содержащими эту точку времени.

Производительность

Проверка пересечения интервалов выполняется за константное время O(1), поскольку включает только сравнение числовых значений временных меток. Это делает функцию пригодной для массовых операций, например, фильтрации списков событий.

При большом количестве интервалов (n × m сравнений) общая сложность становится O(n·m), что следует учитывать при оптимизации календарных систем.

Использование с массивами интервалов

Часто функция применяется в сочетании с методами массивов:

const target = {
  start: new Date(2024, 0, 10),
  end: new Date(2024, 0, 20)
};

const intervals = [
  { start: new Date(2024, 0, 1), end: new Date(2024, 0, 5) },
  { start: new Date(2024, 0, 15), end: new Date(2024, 0, 25) }
];

const overlapping = intervals.filter(interval =>
  areIntervalsOverlapping(target, interval)
);

Результатом становится подмножество пересекающихся интервалов.

Типизация (TypeScript)

При использовании TypeScript структура функции выглядит следующим образом:

interface Interval {
  start: Date | number;
  end: Date | number;
}

interface Options {
  inclusive?: boolean;
}

declare function areIntervalsOverlapping(
  intervalLeft: Interval,
  intervalRight: Interval,
  options?: Options
): boolean;

Это позволяет строго контролировать входные данные и снижать вероятность ошибок при работе с временными диапазонами.

Поведение с различными типами дат

Функция поддерживает как объекты Date, так и числовые метки времени (timestamp). При этом все значения приводятся к числовому виду перед сравнением, что унифицирует поведение независимо от формата входных данных.

areIntervalsOverlapping(
  { start: 1700000000000, end: 1700003600000 },
  { start: new Date(2024, 0, 1), end: new Date(2024, 0, 2) }
);

Такой подход упрощает интеграцию с API, базами данных и внешними источниками времени.