Интервалы в Luxon представлены сущностью Interval,
которая описывает непрерывный промежуток времени между двумя точками
DateTime. В отличие от одиночных дат, интервалы позволяют
работать с диапазонами, которые удобно анализировать, делить, пересекать
и агрегировать. Разбиение интервалов используется в задачах
планирования, тарификации, аналитики временных рядов и построения
расписаний.
Интервал создаётся через
Interval.fromDateTimes(start, end) или через
вспомогательные методы:
import { DateTime, Interval } from "luxon";
const start = DateTime.fromISO("2026-01-01T00:00:00");
const end = DateTime.fromISO("2026-01-10T00:00:00");
const interval = Interval.fromDateTimes(start, end);
Разбиение интервала представляет собой преобразование одного непрерывного диапазона в набор меньших интервалов. Формально это операция:
IntervalIntervalВ Luxon для этого используются методы:
splitAt(...)splitBy(...)Каждый метод ориентирован на разные типы разбиения: по точкам разреза или по равным шагам.
Метод splitAt разделяет интервал в заданных точках
времени. Эти точки должны находиться внутри интервала или на его
границах.
const interval = Interval.fromDateTimes(
DateTime.fromISO("2026-01-01T00:00:00"),
DateTime.fromISO("2026-01-10T00:00:00")
);
const parts = interval.splitAt(
DateTime.fromISO("2026-01-03T00:00:00"),
DateTime.fromISO("2026-01-07T00:00:00")
);
Результатом будет массив из трёх интервалов:
При работе с динамическими наборами точек важно учитывать возможное отсутствие пересечений:
const result = interval.splitAt(
DateTime.fromISO("2025-12-01T00:00:00") // вне интервала
);
В таком случае интервал останется неизменным.
Метод splitBy делит интервал на равные части по заданной
длительности (Duration).
import { Duration } from "luxon";
const interval = Interval.fromDateTimes(
DateTime.fromISO("2026-01-01T00:00:00"),
DateTime.fromISO("2026-01-02T00:00:00")
);
const chunks = interval.splitBy(
Duration.fromObject({ hours: 6 })
);
Результат — четыре интервала по 6 часов.
Алгоритм splitBy работает последовательно:
Формально:
t0 — началоd — шагt0 + k*dЕсли длительность интервала не кратна шагу, последний элемент может быть короче.
const interval = Interval.fromDateTimes(
DateTime.fromISO("2026-01-01T00:00:00"),
DateTime.fromISO("2026-01-01T10:00:00")
);
const parts = interval.splitBy(
Duration.fromObject({ hours: 3 })
);
Результат:
Это поведение важно при расчётах биллинга, где неполные интервалы должны учитываться отдельно.
В реальных сценариях часто используется сочетание
splitAt и splitBy. Сначала выполняется грубое
разбиение по ключевым точкам, затем каждый сегмент дробится
равномерно.
Пример: разделение месяца по неделям с последующим разбиением на дни:
const month = Interval.fromDateTimes(
DateTime.fromISO("2026-01-01"),
DateTime.fromISO("2026-02-01")
);
const weeks = month.splitBy(Duration.fromObject({ days: 7 }));
const daily = weeks.flatMap(week =>
week.splitBy(Duration.fromObject({ days: 1 }))
);
Такая схема используется при построении календарных сеток и аналитических агрегатов.
При разбиении интервалов, содержащих переходы часовых поясов или летнего времени, возможны неоднородные сегменты.
Luxon опирается на DateTime с привязанной зоной:
const interval = Interval.fromDateTimes(
DateTime.fromISO("2026-03-28T00:00:00", { zone: "Europe/Berlin" }),
DateTime.fromISO("2026-03-29T00:00:00", { zone: "Europe/Berlin" })
);
При переходе на летнее время один из шагов может быть короче или
длиннее стандартного часа. Это влияет на результат
splitBy(Duration.fromObject({ hours: 1 })), где количество
сегментов может отличаться от ожидаемого.
Если start равен end, результат разбиения —
пустой набор:
const interval = Interval.fromDateTimes(dt, dt);
interval.splitBy(Duration.fromObject({ hours: 1 })); // []
Если шаг больше интервала, возвращается один элемент:
Результат: [interval]
Использование Duration с неоднозначными единицами
(например, месяцы) может давать переменные результаты, так как длина
месяца не фиксирована:
interval.splitBy(Duration.fromObject({ months: 1 }));
В этом случае границы зависят от календарной модели
DateTime.
При работе с большими диапазонами (например, несколько лет с часовым шагом) количество создаваемых объектов растёт линейно:
DateTimeОптимизация достигается за счёт:
Интервал сессии пользователя дробится на равные отрезки для начисления стоимости:
const session = Interval.fromDateTimes(login, logout);
const billingBlocks = session.splitBy(
Duration.fromObject({ minutes: 15 })
);
Каждый блок затем анализируется отдельно.
Для агрегации событий по дням:
const range = Interval.fromDateTimes(
DateTime.fromISO("2026-01-01"),
DateTime.fromISO("2026-01-31")
);
const days = range.splitBy(Duration.fromObject({ days: 1 }));
Каждый день становится бакетом для статистики.
При планировании задач интервал разбивается на слоты:
const schedule = dayInterval.splitBy(
Duration.fromObject({ minutes: 30 })
);
Разбиение не выходит за пределы исходного интервала. Каждый подинтервал автоматически ограничивается:
Даже если шаг пересекает границу, лишняя часть отбрасывается.
| Характеристика | splitAt | splitBy |
|---|---|---|
| Тип разбиения | по точкам | по шагу |
| Вход | DateTime | Duration |
| Равномерность | нет | да |
| Использование | события, границы | сегментация, тайм-слоты |
Разбиение интервалов в Luxon образует основу для построения детерминированных временных структур, где важны точность границ, предсказуемость сегментации и корректная работа с календарными особенностями.