Для начала, я бы преобразовал даты в формат, удобный для сравнения:
spoiler$intervals =
[['start' => '2017-01-01', 'end' => '2017-01-06'],
['start' => '2017-01-21', 'end' => '2017-01-30'],
['start' => '2017-01-10', 'end' => '2017-01-20'],
['start' => '2017-01-05', 'end' => '2017-01-16'],
['start' => '2017-01-15', 'end' => '2017-01-20']];
Затем нужна функция, определяющая пересечение интервалов и функция, объединяющая интервалы с добавлением подинтервалов:
spoilerfunction is_intersect($int1, $int2) {
return ($int2['end'] >= $int1['start'] && $int1['end'] >= $int2['start']);
}
function combine($int1, $int2) {
if (!isset($int1['subintervals'])) {
$int1['subintervals'] = [$int1];
}
if (!isset($int2['subintervals'])) {
$int2['subintervals'] = [$int2];
}
return array('start' => min($int1['start'], $int2['start']), 'end' => max($int1['end'], $int2['end']),
'subintervals' => array_merge($int1['subintervals'], $int2['subintervals']));
}
Теперь пишем функцию, один раз проверяющую все интервалы на пересечения:
spoilerfunction combine_intersected($intervals) {
$result = [];
$noIntersects = true;
foreach ($intervals as $int1) {
$combined = false;
foreach ($result as &$int2) {
if (is_intersect($int1, $int2)) {
$int2 = combine($int1, $int2);
$noIntersects = false;
$combined = true;
break;
}
}
if (!$combined) {
$result[] = $int1;
}
}
if ($noIntersects) {
return false;
}
return $result;
}
Ну и, напоследок, цикл, объединяющий интервалы:
spoilerwhile ($ints = combine_intersected($intervals)) {
$intervals = $ints;
}
Запускаем:
spoilerArray (
[0] => Array (
[start] => 2017-01-01
[end] => 2017-01-20
[subintervals] => Array (
[0] => Array (
[start] => 2017-01-10
[end] => 2017-01-20
)
[1] => Array (
[start] => 2017-01-15
[end] => 2017-01-20
)
[2] => Array (
[start] => 2017-01-05
[end] => 2017-01-16
)
[3] => Array (
[start] => 2017-01-01
[end] => 2017-01-06
)
)
)
[1] => Array (
[start] => 2017-01-21
[end] => 2017-01-30
)
)