An arbitrary set of calendar ranges may contain overlaps and nested intervals. Sorting by start time makes those relationships local. A left-to-right sweep then carries one current block: extend it when the next interval overlaps, or emit it when a gap appears.

Visualization

The contained interval [3,5] leaves [1,6] unchanged, which makes the max(current.end, next.end) rule visible. The gaps before [8,10] and [13,16] finalize the earlier blocks, while the closed intervals [13,16] and [16,20] merge at their shared endpoint.

The sweep carries one piece of state: current, the interval being accumulated, initialised to the first interval after sorting. For each following next:

  • next.start <= current.end means the two overlap, so current.end = max(current.end, next.end). The max matters because next can be fully contained inside current — merging [1,10] with [2,3] must stay [1,10], not shrink to [1,3].
  • Otherwise a gap separates them: current can never grow again, so it is emitted and current becomes next.

The reason one comparison suffices is the sort. After ascending starts, every interval later in the list starts at or after next.start. If next does not reach current.end, no interval after it can reach back either, so current is final the moment a gap appears. The invariant that survives each step is that current.end holds the furthest right edge of every interval merged into the current block, which is exactly the value the next overlap test needs.

The same sort-then-sweep skeleton answers the rest of the interval family, each specialising the emit/extend step:

  • Insert one interval into a sorted, pairwise-disjoint list — the sweep copies intervals ending before the new one, then expands the pending interval to [min(starts), max(ends)] for every overlap under the chosen closed or half-open endpoint convention. It copies the remaining intervals after that run. No re-sort is needed. If the existing list overlaps itself, normalize it first.
  • Intersect two sorted, pairwise-disjoint lists — a Two Pointers sweep advances the pointer whose interval ends first and emits [max(starts), min(ends)] whenever the current pair overlaps. Ordering and internal non-overlap on both sides keep the scan to one pass; otherwise each list needs normalization first.
Complexity
n
number of input intervals

When the Convention or Order Breaks

The overlap rule comes from the interval convention. Closed intervals [1,2] and [2,3] share the point 2, so they merge. Half-open ranges [1,2) and [2,3) do not overlap. The code expresses that choice as <= for closed intervals or < for half-open ones. Scheduling usually uses half-open ranges so one meeting may start when another ends.

The sweep is correct only after sorting by start. On [[1,3],[6,8],[2,5]], an unsorted pass emits [1,3] when it sees the gap before [6,8], then encounters [2,5] too late to form [1,5]. Sorting by end does not restore the needed invariant: later intervals must have starts no earlier than the one currently being processed.

Diagram and C# Implementation

Comparison

StrategyPreconditionBest fit
Sort then mergeNoneCollapse one static set
Brute-force pairwiseNoneTiny inputs where simplicity dominates
Insert into a merged listSorted, pairwise-disjoint inputAdd one interval to an already-normalized list
Intersect two merged listsBoth inputs sorted and pairwise-disjointProduce only shared coverage

Sort then merge is the reliable default for an arbitrary static set. Insert and intersection variants earn their single pass only when their inputs are already sorted and pairwise disjoint.

References