В Luxon интервал представляет собой непрерывный отрезок времени,
заданный двумя границами: началом и концом. Внутренне это объект,
который хранит две сущности типа DateTime —
start и end.
Ключевая особенность модели интервалов Luxon заключается в строгой неизменяемости границ и формальной корректности: интервал считается валидным только тогда, когда начальная точка строго предшествует конечной.
Основная идея:
[start, end), где начало
включено, а конец исключёнТакое представление позволяет избежать неоднозначностей при работе с соседними интервалами.
Базовый способ формирования интервала — использование двух объектов
DateTime.
import { DateTime, Interval } from "luxon";
const start = DateTime.local(2026, 1, 1, 10, 0);
const end = DateTime.local(2026, 1, 1, 12, 0);
const interval = Interval.fromDateTimes(start, end);
При создании через fromDateTimes Luxon выполняет
проверку:
start <= end — интервал валиденstart > end — интервал становится
невалиднымНевалидный интервал не выбрасывает исключение, а помечается как
invalid.
После создания интервала ключевыми точками доступа являются свойства:
interval.start;
interval.end;
Эти значения возвращаются как DateTime и сохраняют всю
информацию о времени, включая:
Важно учитывать, что start и end не
копируются при изменении интервала — любой метод, модифицирующий
интервал, возвращает новый объект.
Luxon использует полуоткрытую модель:
[start, end)
Это означает:
start входит в интервалend не входит в интервалТакой подход критически важен при вычислениях пересечений и объединений интервалов.
Пример:
const a = Interval.fromDateTimes(
DateTime.fromISO("2026-01-01T10:00"),
DateTime.fromISO("2026-01-01T11:00")
);
const b = Interval.fromDateTimes(
DateTime.fromISO("2026-01-01T11:00"),
DateTime.fromISO("2026-01-01T12:00")
);
Интервалы a и b не пересекаются, несмотря
на общую точку 11:00.
Каждый интервал имеет свойство:
interval.isValid
Если границы заданы некорректно, интервал считается невалидным.
const invalid = Interval.fromDateTimes(
DateTime.local(2026, 1, 2),
DateTime.local(2026, 1, 1)
);
invalid.isValid; // false
Дополнительно доступна диагностика причины:
invalid.invalidReason;
invalid.invalidExplanation;
Чаще всего встречается причина:
end before startLuxon поддерживает создание интервалов из ISO-формата:
const interval = Interval.fromISO("2026-01-01T10:00/2026-01-01T12:00");
Строка содержит две границы, разделённые символом /.
Особенности:
DateTimeЛюбое изменение интервала приводит к созданию нового объекта.
Пример изменения начала и конца:
const interval2 = interval.set({
start: DateTime.local(2026, 1, 1, 9, 0),
end: DateTime.local(2026, 1, 1, 13, 0)
});
Исходный интервал при этом остаётся неизменным.
Такая модель предотвращает:
start и end всегда являются полноценными
DateTime объектами, включая таймзону.
Это означает:
Пример:
const start = DateTime.fromISO("2026-01-01T10:00", { zone: "UTC" });
const end = DateTime.fromISO("2026-01-01T10:00", { zone: "Europe/Moscow" });
const interval = Interval.fromDateTimes(start, end);
Фактически end может быть позже или раньше
start в абсолютном времени, несмотря на одинаковое
локальное отображение.
const i = Interval.fromDateTimes(
DateTime.local(2026, 1, 1, 10, 0),
DateTime.local(2026, 1, 1, 10, 0)
);
Результат:
Luxon допускает интервалы с минимальной разницей:
const i = Interval.fromDateTimes(
DateTime.local(2026, 1, 1, 10, 0, 0, 0),
DateTime.local(2026, 1, 1, 10, 0, 0, 1)
);
Такие интервалы часто используются для событий с высокой точностью.
При работе с внешними источниками данных часто требуется нормализация:
Пример приведения к началу дня:
const start = DateTime.local(2026, 1, 1).startOf("day");
const end = DateTime.local(2026, 1, 7).endOf("day");
const interval = Interval.fromDateTimes(start, end);
startOf и endOf формируют логически
выровненные границы, что важно для календарных диапазонов.
Начало и конец интервала активно участвуют в операциях пересечения:
Логика всех этих операций базируется на строгом сравнении
start и end как абсолютных моментов
времени.
Luxon позволяет менять начало или конец независимо:
const shiftedStart = interval.set({ start: DateTime.local(2026, 1, 1, 8, 0) });
const shiftedEnd = interval.set({ end: DateTime.local(2026, 1, 1, 14, 0) });
При этом проверка валидности выполняется заново:
Длина интервала напрямую вычисляется из разницы:
interval.length;
Она определяется как:
end - start в миллисекундахЛюбое изменение границ автоматически влияет на длину, но не изменяет
исходные DateTime объекты.
Luxon требует наличия обеих границ. Интервал без start
или end не может считаться валидным объектом.
Если данные приходят частично, создаётся невалидный интервал:
Interval.fromDateTimes(DateTime.local(2026, 1, 1));
Результат:
isValid === falseНачало и конец используются при сравнении интервалов:
Пример касания:
a.end.equals(b.start);
При полуоткрытой модели это не считается пересечением.
Luxon опирается на миллисекундную точность:
При передаче start и end в другие структуры
следует учитывать:
DateTime — иммутабеленDateTimeЭто упрощает управление интервалами в сложных системах планирования и расписаний