Операции с интервалами

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

Интервалы реализованы классом Interval, который оперирует объектами DateTime и предоставляет набор методов для геометрии времени: пересечения, объединения, разбиения и проверки включений.


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

Базовая форма интервала задаётся двумя моментами времени:

import { DateTime, Interval } from "luxon";

const start = DateTime.fromISO("2026-01-01T10:00:00");
const end = DateTime.fromISO("2026-01-01T12:00:00");

const interval = Interval.fromDateTimes(start, end);

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

Дополнительные способы создания:

const interval = Interval.fromISO("2026-01-01T10:00:00/2026-01-01T12:00:00");
const now = DateTime.now();
const interval = Interval.after(now, { hours: 2 });
const interval = Interval.before(now, { days: 1 });

Проверка валидности и границ

Интервал содержит свойства start, end и флаг валидности:

interval.isValid;
interval.start;
interval.end;

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

if (!interval.isValid) {
  // обработка некорректного диапазона
}

Длина интервала

Длительность интервала вычисляется относительно единиц времени:

interval.length("hours");
interval.length("minutes");
interval.length("days");

Результат зависит от календаря и часовых поясов, поскольку используется модель DateTime.


Проверка принадлежности времени интервалу

Метод contains определяет, входит ли точка во временной диапазон:

interval.contains(DateTime.fromISO("2026-01-01T11:00:00"));

Семантика включает начало и исключает конец: [start, end).


Отношения между интервалами

Пересечение

Проверка наличия пересечения:

interval1.overlaps(interval2);

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


Получение пересечения

Формирование нового интервала из общей части:

const common = interval1.intersection(interval2);

Если пересечения нет, результатом становится null.


Объединение

Объединение возможно при пересечении или соприкосновении:

const merged = interval1.union(interval2);

Если интервалы не связаны, результатом становится массив интервалов.


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

Метод merge объединяет массив интервалов:

const result = Interval.merge([i1, i2, i3]);

Соседние и пересекающиеся диапазоны объединяются в минимальное количество непрерывных отрезков.


Разность интервалов

Операция XOR возвращает части, не входящие в пересечение:

const diff = interval1.xor(interval2);

Результатом становится массив интервалов, отражающих «исключённые» области.


Проверка включения и охвата

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

interval1.engulfs(interval2);

Истинно, если второй интервал целиком содержится внутри первого.


Граничное касание

interval1.abutsStart(interval2);
interval1.abutsEnd(interval2);

Используется для проверки соприкосновения без пересечения.


Разбиение интервалов

Разбиение по точкам

const parts = interval.splitAt(DateTime.fromISO("2026-01-01T11:00:00"));

Интервал делится в указанных точках на несколько частей.


Разбиение по шагу

const chunks = interval.splitBy({ minutes: 30 });

Создаются равные сегменты заданной длительности.


Сдвиг и преобразование границ

Метод mapEndpoints позволяет преобразовать границы интервала:

const shifted = interval.mapEndpoints(
  dt => dt.plus({ hours: 1 }),
  dt => dt.plus({ hours: 1 })
);

Применяется для синхронного смещения начала и конца.


Изменение интервала

Метод set создаёт новый интервал с изменёнными параметрами:

const upd ated = interval.se t({
  start: start.plus({ hours: 1 })
});

Оригинальный интервал остаётся неизменным.


Итерация по интервалу

Интервал может быть использован как основа для генерации последовательностей времени через разбиение и длину шагов. Комбинация splitBy и count позволяет строить дискретные временные сетки.


Форматирование интервалов

Строковое представление:

interval.toISO();

Пример результата:

2026-01-01T10:00:00.000Z/2026-01-01T12:00:00.000Z

Также доступны специализированные форматы:

interval.toISODate();
interval.toISOTime();

Работа с массивами интервалов

Множественные интервалы часто нормализуются через:

  • merge — объединение пересекающихся
  • xor — выделение разницы
  • union — объединение двух интервалов

Типичный сценарий обработки расписаний:

const busy = Interval.merge(meetings);
const free = busy.xor(workDayInterval);

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

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

  • пересечение определяет конфликт
  • включение определяет вложенность
  • касание определяет непрерывность

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


Поведение с часовыми поясами

Интервалы зависят от DateTime, поэтому при смене временной зоны границы могут смещаться. Корректная модель требует фиксированного контекста времени (UTC или единый zoneId), иначе операции пересечения могут давать неожиданные результаты.


Неопределённые и частичные интервалы

Интервал обязан иметь обе границы. Отсутствие одной из них приводит к невалидному состоянию. Для открытых диапазонов используется комбинация с Infinity логикой на уровне приложения, а не внутри класса.


Композиция операций

Комбинирование методов формирует сложные преобразования:

const available = Interval
  .merge(busyIntervals)
  .xor(workInterval)
  .map(i => i.set({ start: i.start.plus({ minutes: 15 }) }));

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