Я работаю над функцией, которая проверяет, сталкиваются ли внешние события с внутренними событиями (в приложении для календаря). Процесс выглядит следующим образом:
$internalEvents ) $externalEvents ).  Это существующие события с типом busy . Я старался как можно меньше сократить его. Это ввод данных:
 $internalEvents = array( array( "start" => "03/29/2016 12:00:00", "end" => "03/29/2016 13:00:00" ), array( "start" => "03/29/2016 12:30:00", "end" => "03/29/2016 13:30:00" ), array( "start" => "03/29/2016 13:00:00", "end" => "03/29/2016 14:00:00" ), array( "start" => "03/29/2016 13:30:00", "end" => "03/29/2016 14:50:00" ), array( "start" => "03/29/2016 14:00:00", "end" => "03/29/2016 15:00:00" ), array( "start" => "03/29/2016 14:30:00", "end" => "03/29/2016 15:30:00" ), array( "start" => "03/29/2016 15:00:00", "end" => "03/29/2016 16:00:00" ), array( "start" => "03/29/2016 15:30:00", "end" => "03/29/2016 16:30:00" ), array( "start" => "03/29/2016 16:00:00", "end" => "03/29/2016 17:00:00" ) ); $externalEvents = array( array( "start" => "03/29/2016 08:00:00", "end" => "03/29/2016 12:00:00", "type" => "busy" ), array( "start" => "03/29/2016 15:30:00", "end" => "03/29/2016 16:00:00", "type" => "busy" ), array( "start" => "03/29/2016 13:30:00", "end" => "03/29/2016 14:15:00", "type" => "busy" ) ); 
Теперь я пытаюсь найти любой конфликт, сравнивая внутреннее событие со всеми внешними событиями:
 foreach($internalEvents as $internalEvent) { $internalEventStart = new DateTime($internalEvent['start']); $internalEventEnd = new DateTime($internalEvent['end']); $result = true; echo "\nverifying " . $internalEventStart->format('Ymd H:i') . " - " . $internalEventEnd->format('Ymd H:i') . "\n"; foreach($externalEvents as $externalEvent) { $externalEventStart = new DateTime($externalEvent['start']); $externalEventEnd = new DateTime($externalEvent['end']); // check if there are conflicts between internal and external events if ($internalEventStart >= $externalEventStart && $internalEventStart <= $externalEventEnd) { $result = false; echo " problem 1: event is between busy time: " . "\n"; } if ($internalEventStart >= $externalEventStart && $internalEventStart <= $externalEventEnd && $externalEventEnd <= $internalEventEnd) { $result = false; echo " problem 2: event starts during busy time: " . "\n"; } if ($internalEventStart <= $externalEventStart && $externalEventStart <= $internalEventEnd && $internalEventEnd <= $externalEventEnd) { $result = false; echo " problem 3: event stops during busy time: " . "\n"; } if (($internalEventStart <= $externalEventStart) && ($externalEventStart <= $externalEventEnd) && ($externalEventEnd <= $internalEventEnd)) { $result = false; echo " problem 4: event during busy time: " . "\n"; } if (($internalEventStart <= $internalEventEnd) && ($internalEventEnd <= $externalEventStart) && ($externalEventStart <= $externalEventEnd)) { $result = false; echo " problem 5: event during busy time: " . "\n"; } } if($result) { echo " result: OK\n"; } else { echo " result: NOT OK \n"; } } 
Я ищу алгоритм, который может найти любой возможный конфликт перекрытия событий. Любой намек высоко ценится.
Прогон кода можно найти здесь (IDEone.com).
Когда сталкиваются два события? См. Эту схему:
  ----E---- CS/EE CE/ES --N-- < < --N-- > > --C-- < > --C-- < > --C-- < > -----C----- < > ·················································· E = Main Event N = Not Collide Event C = Collide Event CS = Compare Event Start EE = Main Event End CE = Compare Event End ES = Main Event Start 
Как вы можете видеть, столкновение происходит только тогда, когда начало события C до конца события E, а конец события E – после начала события C. Знание этого помогает найти эффективный и короткий метод для сравнения событий.
О коде, предварительное примечание: вы конвертируете даты в ISO 8601, прежде чем сравнивать их несколько раз в своем коде, так почему бы не создать функцию для этого?
 function eventsToDate( $row ) { $retval = array( 'start' => date_create( $row['start'] )->format('Ymd H:i:s'), 'end' => date_create( $row['end'] )->format('Ymd H:i:s') ); $retval['print'] = sprintf( '%s-%s', substr( $retval['start'],-8,5 ), substr( $retval['end'],-8,5 ) ); return $retval; } 
Эта функция возвращает ассоциативный массив с 'start' и 'end' в вашем формате. Я добавил третий ключ, 'print', чтобы использовать его во время отладки. Обратите внимание, что в печати я рассматриваю только часы: минуты (все даты в вашем примере массива с одного дня), но сравнение производится в полные даты. Вы можете опустить этот «печатный» ключ или заменить его предпочтительным форматом вывода.
  Вы выполняете два вложенных foreach , и для каждого цикла пересчитываете формат даты.  С образцами массива вы вызываете DateTime / DateTime :: format 36 раз.  Создав временный массив со всеми преобразованными $externalEvents , мы можем уменьшить эти вызовы до 12. Итак, перед запуском цикла foreach() мы используем array_map с array_map функцией и массивом $externalEvents для создания массива с отформатированными датами: 
 $externalDates = array_map( 'eventsToDate', $externalEvents ); 
  Затем мы начинаем основной цикл foreach() на $internalEvents : 
 foreach( $internalEvents as $internalEvent ) { $internalDates = eventsToDate( $internalEvent ); echo $internalDates['print'] . PHP_EOL; $result = True; foreach( $externalDates as $externalDate ) { 
  На данный момент мы сравниваем даты.  Как упоминалось выше, мы начинаем с начала и конца с начала.  Чтобы упростить следующее сравнение, мы используем strcmp , php, которая «возвращает <0, если str1 меньше str2,> 0, если str1 больше str2, а 0, если они равны»: 
  $startCmp = strcmp( $internalDates['start'], $externalDate['end'] ); $endCmp = strcmp( $internalDates['end'], $externalDate['start'] ); 
Теперь, сравнение:
  if( $startCmp<0 && $endCmp>0 ) { $result = False; echo " {$externalDate['print']} COLLIDE\n"; } else { echo " {$externalDate['print']} OK\n"; } } 
И, наконец, мы можем напечатать результат:
  echo "Result: " . ( $result ? 'OK' : 'NOT OK') . "\n\n"; } 
Демо-версия eval.in
  Примечание: при сравнении выше, с первым $internalEvent получаем следующий результат: 
 12:00-13:00 08:00-12:00 OK 15:30-16:00 OK 13:30-14:15 OK Result: OK 
Вместо этого, если вы хотите получить этот результат:
 12:00-13:00 08:00-12:00 COLLIDE 15:30-16:00 OK 13:30-14:15 OK Result: NOT OK 
  Вы должны заменить выше, if условие: 
  if( $startCmp<=0 && $endCmp>=0 ) 
  Над кодом будет работать, если вы хотите получить более подробную информацию о виде столкновения, вы можете проверить другие комбинации начала и конца внутри условия if . 
Альтернатива: Возвращающиеся сталкивающиеся события
  Если вместо печати результатов вы хотите поймать встречные события, вы можете заменить вложенный foreach() на array_filter таким образом: 
 $result = array_filter ( $externalDates, function( $row ) use( $internalDates ) { $startCmp = strcmp( $internalDates['start'], $row['end'] ); $endCmp = strcmp( $internalDates['end'], $row['start'] ); return( $startCmp<0 && $endCmp>0 ); } ); 
  На этом этапе встречные события находятся в массиве $result .  Очевидно, что массив пуст, конфликтов нет.