php 51 lines · 8 steps

Merging overlapping busy time blocks in PHP

A service that normalizes calendar events and collapses overlapping intervals into clean busy blocks.

Explained by highlit
1<?php
2 
3namespace App\Services;
4 
5use DateTimeImmutable;
6 
7final class BusyBlockMerger
8{
9 public function merge(array $events): array
10 {
11 $ranges = [];
12 
13 foreach ($events as $event) {
14 $start = $event['start'] instanceof DateTimeImmutable
15 ? $event['start']
16 : new DateTimeImmutable($event['start']);
17 $end = $event['end'] instanceof DateTimeImmutable
18 ? $event['end']
19 : new DateTimeImmutable($event['end']);
20 
21 if ($end <= $start) {
22 continue;
23 }
24 
25 $ranges[] = [$start, $end];
26 }
27 
28 usort($ranges, fn (array $a, array $b) => $a[0] <=> $b[0]);
29 
30 $merged = [];
31 
32 foreach ($ranges as [$start, $end]) {
33 $last = array_key_last($merged);
34 
35 if ($last !== null && $start <= $merged[$last]['end']) {
36 if ($end > $merged[$last]['end']) {
37 $merged[$last]['end'] = $end;
38 }
39 continue;
40 }
41 
42 $merged[] = ['start' => $start, 'end' => $end];
43 }
44 
45 return array_map(static fn (array $block) => [
46 'start' => $block['start']->format(DATE_ATOM),
47 'end' => $block['end']->format(DATE_ATOM),
48 'duration_minutes' => (int) (($block['end']->getTimestamp() - $block['start']->getTimestamp()) / 60),
49 ], $merged);
50 }
51}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Sorting intervals by start time turns overlap detection into a single linear pass.
  2. 2Normalizing inputs to a common type early lets the rest of the logic stay simple.
  3. 3Extending the last kept range in place is the whole trick to merging overlaps.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Merging overlapping busy time blocks in PHP — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code