В работе с временными данными в приложениях часто возникает задача приведения набора интервалов к компактному виду. Такие интервалы могут описывать занятость ресурсов, бронирования, окна выполнения задач, периоды активности пользователей. При накоплении большого количества диапазонов неизбежно появляются пересечения и смежные отрезки, которые требуется объединять в единые непрерывные блоки.
В экосистеме js-joda для подобных задач используется модуль расширений с типом Interval, позволяющий моделировать диапазоны времени с высокой точностью и поддержкой строгой типизации временных точек.
Интервал в js-joda задаётся двумя границами: началом и концом. В качестве точек используются типы Instant, LocalDate или LocalDateTime в зависимости от контекста задачи.
Базовая структура интервала:
Такой подход упрощает операции сравнения и объединения, исключая неоднозначность на границах.
import { Instant } from '@js-joda/core'
import { Interval } from '@js-joda/extra'
const start = Instant.parse('2026-01-01T10:00:00Z')
const end = Instant.parse('2026-01-01T12:00:00Z')
const interval = Interval.of(start, end)
Перед объединением диапазонов выполняется анализ их взаимного расположения.
Основные отношения между интервалами:
Эти состояния определяют возможность объединения.
const a = Interval.of(
Instant.parse('2026-01-01T10:00:00Z'),
Instant.parse('2026-01-01T12:00:00Z')
)
const b = Interval.of(
Instant.parse('2026-01-01T11:30:00Z'),
Instant.parse('2026-01-01T13:00:00Z')
)
const overlaps = a.overlaps(b)
const abuts = a.abuts(b)
const connected = a.isConnected(b)
Метод isConnected учитывает как пересечение, так и
смежность, что критично для последующего объединения.
Операция объединения выполняется при наличии связи между интервалами. Результирующий диапазон охватывает минимальную левую границу и максимальную правую границу исходных интервалов.
const a = Interval.of(
Instant.parse('2026-01-01T10:00:00Z'),
Instant.parse('2026-01-01T12:00:00Z')
)
const b = Interval.of(
Instant.parse('2026-01-01T11:00:00Z'),
Instant.parse('2026-01-01T13:30:00Z')
)
const merged = a.union(b)
Полученный интервал:
Если интервалы не связаны, операция объединения становится невозможной в рамках строгой модели и требует предварительной обработки.
При работе с массивами интервалов задача усложняется необходимостью нормализации данных. Типовой алгоритм включает сортировку и последовательное слияние.
const sorted = intervals.sort((i1, i2) =>
i1.start().compareTo(i2.start())
)
Последовательное объединение выполняется путём накопления результата в отдельном массиве.
const result = []
for (const current of sorted) {
if (result.length === 0) {
result.push(current)
continue
}
const last = result[result.length - 1]
if (last.isConnected(current)) {
const merged = last.union(current)
result[result.length - 1] = merged
} else {
result.push(current)
}
}
Особенность модели Interval заключается в том, что смежные интервалы рассматриваются как потенциально объединяемые. Это позволяет устранять микропромежутки, возникающие из-за дискретности данных.
Пример набора:
После обработки:
Первый и второй интервалы объединяются за счёт смежности границ.
Перед выполнением операции полезно использовать предикаты состояния интервалов:
overlaps — пересечениеabuts — касание границisConnected — обобщённая связностьif (a.isConnected(b)) {
const merged = a.union(b)
}
Такой подход снижает количество лишних вычислений и упрощает ветвление логики.
При большом количестве интервалов возможны цепочки объединений, когда результат одного слияния влияет на последующие проверки.
Пример:
После последовательного применения объединения формируется один интервал:
Алгоритмически это достигается за счёт постоянного сравнения текущего интервала с последним элементом результата.
При использовании LocalDate диапазоны описывают календарные периоды без учёта времени суток.
import { LocalDate } from '@js-joda/core'
import { Interval } from '@js-joda/extra'
const d1 = LocalDate.parse('2026-01-01')
const d2 = LocalDate.parse('2026-01-10')
const interval = Interval.of(d1.atStartOfDay(), d2.atStartOfDay())
При объединении таких интервалов логика остаётся идентичной, однако важно учитывать календарные особенности, включая переходы между месяцами и годами.
При объединении диапазонов необходимо учитывать:
Вложенные интервалы не расширяют общий диапазон:
const outer = Interval.of(
Instant.parse('2026-01-01T10:00:00Z'),
Instant.parse('2026-01-01T15:00:00Z')
)
const inner = Interval.of(
Instant.parse('2026-01-01T11:00:00Z'),
Instant.parse('2026-01-01T12:00:00Z')
)
const merged = outer.union(inner)
Результат сохраняет границы outer.
При обработке тысяч интервалов ключевым фактором становится сложность сортировки и линейного прохода. Итоговая сложность алгоритма:
Это делает подход применимым для систем планирования, аналитики и календарных вычислений без дополнительных структур данных.
Объединение диапазонов применяется при:
Во всех случаях ключевым этапом становится приведение множества перекрывающихся интервалов к минимальному набору непрерывных блоков, что упрощает последующую обработку и анализ данных.