Pergunta

There are many posts about checking overlapping between two dates. However I couldn't find any which talks about how to check among multple ranges.

Say I have this array:

$ranges = [
array('start'=>'2014-01-01' , 'end'=> '2014-01-05'),
array('start'=>'2014-01-06' > , 'end'=> '2014-01-10'),
array('start'=>'2014-01-04' > , 'end'=> '2014-01-07')]

One may simply think that a function which checks overlapping between two ranges can work with a loop among all ranges, but this is wrong, because all ranges can overlap with each others, but not overlap all together.

I hope someone can help me to find a good solution.....

Foi útil?

Solução 2

<?php

// pass your ranges to this method and if there is a common intersecion it will
// return it or false

function checkIfOverlapped($ranges)
{
    $res = $ranges[0];

    $countRanges = count($ranges);

    for ($i = 1; $i < $countRanges; $i++) {

        $r1s = $res['start'];
        $r1e = $res['end'];

        $r2s = $ranges[$i]['start'];
        $r2e = $ranges[$i]['end'];

        if ($r1s >= $r2s && $r1s <= $r2e || $r1e >= $r2s && $r1e <= $r2e || $r2s >= $r1s && $r2s <= $r1e || $r2e >= $r1s && $r2e <= $r1e) {

            $res = array(
                'start' => $r1s > $r2s ? $r1s : $r2s,
                'end' => $r1e < $r2e ? $r1e : $r2e
            );

        } else return false;

    }

    return $res;
}

// example
$ranges = array(
    array('start' => '2014-01-01', 'end' => '2014-01-05'),
    array('start' => '2014-01-05', 'end' => '2014-01-10'),
    array('start' => '2014-01-04', 'end' => '2014-01-07')
);

var_dump(checkIfOverlapped($ranges));

Outras dicas

None of the above solutions really worked for me, I am using Carbon API here for date comparison but this can be achieved by normal PHP date comparison if you are not using Carbon API.

PS: Happy if someone can optimize this code.

public static function checkOverlapInDateRanges($ranges) {
    $overlapp = [];
    for($i = 0; $i < count($ranges); $i++){
        for($j= ($i + 1); $j < count($ranges); $j++){

            $start = \Carbon\Carbon::parse($ranges[$j]['start']);
            $end = \Carbon\Carbon::parse($ranges[$j]['end']);

            $start_first = \Carbon\Carbon::parse($ranges[$i]['start']);
            $end_first = \Carbon\Carbon::parse($ranges[$i]['end']);

            if(\Carbon\Carbon::parse($ranges[$i]['start'])->between($start, $end) || \Carbon\Carbon::parse($ranges[$i]['end'])->between($start, $end)){
                $overlapp[] = $ranges[$j];
                break;
            }
            if(\Carbon\Carbon::parse($ranges[$j]['start'])->between($start_first, $end_first) || \Carbon\Carbon::parse($ranges[$j]['end'])->between($start_first, $end_first)){
                $overlapp[] = $ranges[$j];
                break;
            }
        }
    }
    return $overlapp;
}

based on Raeef Refai answer the algorithm only checks dates next to each other but not as a whole list of dateranges. here a tweaked version. hope this helps.

$ranges = [
    ['2014-01-01','2014-01-05'],
    ['2014-01-05','2014-01-10'],
    ['2014-01-04','2014-01-07']
];

foreach($ranges as $key => $range){

  $r1s = $range[0];
  $r1e = $range[1];

  foreach($ranges as $key2 => $range2){
    if($key != $key2){
      $r2s = $range2[0];
      $r2e = $range2[1];

      if ($r1s >= $r2s && $r1s <= $r2e || $r1e >= $r2s && $r1e <= $r2e || $r2s >= $r1s && $r2s <= $r1e || $r2e >= $r1s && $r2e <= $r1e) {

          $res = array(
              '0' => $r1s > $r2s ? $r1s : $r2s,
              '1' => $r1e < $r2e ? $r1e : $r2e
          );
          break;
      }
    }
  }
}

I convert the Carbon answer of Reef in pure PHP

function checkOverlapInDateRanges($ranges) {
    
    $overlapp = [];
    
    for($i = 0; $i < count($ranges); $i++){
        
        for($j= ($i + 1); $j < count($ranges); $j++){

            $start_a = strtotime($ranges[$i]['start']);
            $end_a = strtotime($ranges[$i]['end']);

            $start_b = strtotime($ranges[$j]['start']);
            $end_b = strtotime($ranges[$j]['end']);

            if( $start_b <= $end_a && $end_b >= $start_a ) {
                $overlapp[] = "i:$i j:$j " .$ranges[$i]['start'] ." - " .$ranges[$i]['end'] ." overlap with " .$ranges[$j]['start'] ." - " .$ranges[$j]['end'];
                break;
            }
            
        }
        
    }
    
    return $overlapp;
    
}

// example
$ranges = array(
    array('start' => '2022-01-01', 'end' => '2022-01-05'),
    array('start' => '2022-01-05', 'end' => '2022-01-10'),
    array('start' => '2022-01-9', 'end' => '2022-01-15'),
    array('start' => '2022-01-13', 'end' => '2022-01-15')
);

echo "<pre>";
var_dump(checkOverlapInDateRanges($ranges));
echo "</pre>";
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top