If you don't count at all and just sort by month, choosing min-max then:
$struct = []; $items = [ ['fromDate' => '2016-01-01', 'toDate' => '2016-01-12'], ['fromDate' => '2016-02-02', 'toDate' => '2016-02-12'], ['fromDate' => '2016-03-01', 'toDate' => '2016-03-01'], ['fromDate' => '2016-01-05', 'toDate' => '2016-01-16'] ]; //объединим все даты в 1 массив $dates = array_merge(array_column($items , 'fromDate') , array_column($items, 'toDate')); //разложим все даты по месяцам array_map(function($date)use(&$struct){ $m = new DateTime($date); $struct[$m->format('m')][] = $date; }, $dates); //выбираем min max дату в месяце $items = array_map(function($range){ return [ 'fromDate' => min($range),'toDate' => max($range) ]; }, $struct); print_r($items);
at the exit
[01] => Array ( [fromDate] => 2016-01-01 [toDate] => 2016-01-16 ) [02] => Array ( [fromDate] => 2016-02-02 [toDate] => 2016-02-12 ) [03] => Array ( [fromDate] => 2016-03-01 [toDate] => 2016-03-01 )
In connection with the conditions clarified in the dialogue, I update the post, now it is absorbed regardless of the month, the entire range of dates is taken into account.
$items = [ ['fromDate' => '2016-01-01', 'toDate' => '2016-01-12'], ['fromDate' => '2016-02-02', 'toDate' => '2016-02-12'], ['fromDate' => '2016-02-12', 'toDate' => '2016-02-22'], ['fromDate' => '2016-03-01', 'toDate' => '2016-03-01'], ['fromDate' => '2016-01-03', 'toDate' => '2016-01-10'], ['fromDate' => '2016-01-05', 'toDate' => '2016-01-16'], ['fromDate' => '2013-10-10', 'toDate' => '2013-12-13'], ['fromDate' => '2012-01-01', 'toDate' => '2015-03-10'], ]; $startDates = array_column($items , 'fromDate'); array_walk($items, function(&$item)use($startDates , &$items){ //диапазон всех дат входящих в период $end = new DateTime($item['toDate']); $period = new DatePeriod( new DateTime($item['fromDate']), new DateInterval('P1D'), $end->modify("+1 day"), DatePeriod::EXCLUDE_START_DATE ); //итератор периода foreach ($period as $date) { $search = $date->format('Ym-d'); //ищем наличие пересечения, если оно есть удаляем диапазон из массива , //если в удаляемом массиве конечная дата выше , обновляем ее $id = array_search($search , $startDates); if($id !== false){ if($item['toDate'] < $items[$id]['toDate']){ $item['toDate'] = $items[$id]['toDate']; } unset($items[$id]); } } }); print_r($items);