如何在PHP中检查多个日期范围之间的重叠?

7

有很多帖子关于如何检查两个日期之间的重叠。但是我找不到任何一个谈论如何在多个范围之间进行检查。

假设我有这个数组:

$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')]

有些人可能认为一个检查两个范围之间重叠的函数可以通过循环遍历所有范围来实现,但这是错误的,因为所有范围之间可能相互重叠,但不可能全部同时重叠。

我希望有人能帮我找到一个好的解决方案......

4个回答

3

以上的解决方案对我并没有真正起到作用,我这里使用Carbon API进行日期比较,但如果你不使用Carbon API,也可以通过普通的PHP日期比较来实现。

提示:如果有人能优化一下这段代码,我会很开心。

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;
}

2
要用本机PHP代码替换“between”函数,您可以使用if ($start_first <= $end && $end_first >= $start) { // overlap } - 4thfloorstudios

1
<?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));

如果在循环的第一次迭代中没有重叠,函数将返回false,并且不会进行进一步的比较。 - cmcnulty
1
此函数用于查找公共交集,如果前两个没有交集,则返回false。 - Raeef Refai
谢谢您的解释。我理解OP想知道是否有任何重叠,而不是所有重叠,但重新阅读后,很难确定OP想要哪种情况。 - cmcnulty

0

我将Reef的Carbon答案转换为纯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>";

0
基于 Raeef Refai 的回答,该算法仅检查相邻的日期而不是整个日期范围列表。这里是一个调整过的版本。希望可以帮到您。
$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;
      }
    }
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接