dousi2029
dousi2029
2017-04-06 09:23

两个日期范围重叠 - 1501人失踪? [PHP]

已采纳

https://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap

I wrote this post to be sure am I wrong or no one saw the bug here.

To be sure I collect all the data in functions to show results and describe my point.

All was good but when we analise all combination of dates I think there is a bug in combination 6 & 7. Mb it should never happend but when we analise a lot of datas there is point where we don't know and combination 6 will needed.

Function simulate_ranges is to check all posibilities we can check to check the aswer is good or not.

Function stack_overflow_answers - answers from topic to check results.

Ending "for" is to check all answers with all combinations.

Please uncomment other cases to check results and tell me: Am I wrong or topic from top link have wrong math to case 6?

function simulate_ranges($case) {
switch($case)   {
    case 1:
        # A X Z B
        $a='2017-01-01';
        $b='2017-01-04';
        $x='2017-01-02';
        $z='2017-01-03';
        $combo=array('a' => $a,'x' => $x,'z' => $z,'b' => $b);
        $pair=array('a, b' =>$a.' - '.$b, 'x, z' => $x.' - '.$z);
        # ----- START A ------------------------------------------------------------------------------ START B ---  # 
        # -------------------------------- END X ------------------------ END Z -----------------------------------  #
        break;

    case 2:
        # A X B Z
        $a='2017-01-01';
        $b='2017-01-03';
        $x='2017-01-02';
        $z='2017-01-04';
        $combo=array('a' => $a,'x' => $x,'b' => $b,'z' => $z);
        $pair=array('a, b' =>$a.' - '.$b, 'x, z' => $x.' - '.$z);
        # ----- START A ------------------------------------------ START B ---------------------------------------  # 
        # -------------------------------- END X ----------------------------------------------- END Z ------------  #
        break;

    case 3:
        # X A Z B
        $a='2017-01-02';
        $b='2017-01-04';
        $x='2017-01-01';
        $z='2017-01-03';
        $combo=array('x' => $x,'a' => $a,'z' => $z,'b' => $b);
        $pair=array('a, b' =>$a.' - '.$b, 'x, z' => $x.' - '.$z);
        # -------------------------------------- START A --------------------------------------- START B ---------  # 
        # ---------- END X -------------------------------------- END Z -------------------------------------------  #
        break;

    case 4:
        # X A B Z
        $a='2017-01-02';
        $b='2017-01-03';
        $x='2017-01-01';
        $z='2017-01-04';
        $combo=array('x' => $x,'a' => $a,'b' => $b,'z' => $z);
        $pair=array('a, b' =>$a.' - '.$b, 'x, z' => $x.' - '.$z);
        # -------------------------------------- START A ---------------- START B --------------------------------  # 
        # ---------- END X -----------------------------------------------------------  ----------- END Z ---------  #
        break;

    case 5:
        # A B X Z
        $a='2017-01-01';
        $b='2017-01-02';
        $x='2017-01-03';
        $z='2017-01-04';
        $combo=array('a' => $a,'b' => $b,'x' => $x,'z' => $z);
        $pair=array('a, b' =>$a.' - '.$b, 'x, z' => $x.' - '.$z);
        # --------- START A ---------  --------- START B --------- # # ----------------------- ----------------------------------------  # 
        # ---------------------------------------------------------------- #  # ---------- END X -----------  ----------- END Z ---------  #
        break;

    case 6:
        # X Z A B
        $a='2017-01-03';
        $b='2017-01-04';
        $x='2017-01-01';
        $z='2017-01-02';
        # ---------- END X -----------  ----------- END Z ---------  # # ---------------------------------------------------------------- # 
        # ----------------------- ----------------------------------------  # # --------- START A ---------  --------- START B --------- #
        $combo=array('x' => $x,'z' => $z,'a' => $a,'b' => $b);
        $pair=array('a, b' =>$a.' - '.$b, 'x, z' => $x.' - '.$z);
        break;

    case 7:
        # A B X Z
        $a='2017-01-01';
        $b='2017-01-02';
        $x='2017-01-02';
        $z='2017-01-03';
        # --------- START A ---------  --------|- START B -|----------------------------------------------  # 
        # -----------------------------------------|-- END X ---|-------------------------- END Z ---------  #
        $combo=array('a' => $a,'b' => $b,'x' => $x,'z' => $z);
        $pair=array('a, b' =>$a.' - '.$b, 'x, z' => $x.' - '.$z);
        break;

case 8:
    # X Z A B
    $a='2017-01-01 01:00:00';
    $b='2017-01-02 00:00:00';
    $x='2017-01-01 00:00:01';
    $z='2017-01-01 01:00:00';

    # --------- END X ---------  --------|- START A -|----------------------------------------------  # 
    # -----------------------------------------|-- END Z ---|-------------------------- START B ---------  #
    $combo=array('x' => $x,'a' => $a, 'z' => $z,'b' => $b);
    $pair=array('a, b' =>$a.' - '.$b, 'x, z' => $x.' - '.$z);
    break;

}

$a2=strtotime($a);
$b2=strtotime($b);
$x2=strtotime($x);
$z2=strtotime($z);

echo '<table>';
foreach($combo as $var => $data)    {
    $strtotime=${$var.'2'};
    switch($var)        {
        case 'a': $final_var='StartA'; break;
        case 'b': $final_var='StartB'; break;
        case 'x': $final_var='EndA'; break;
        case 'z': $final_var='EndB'; break;
    }
    echo '<tr><td style="text-align: right;"> ('.$final_var.') </td><td>&rarr; '.$data.'</td><td> ('.$strtotime.')</td></tr>';
}
echo '</table>';

echo '<table><tr>';
$i=0;
foreach($pair as $vars => $dates_ranges)    {
    switch($vars)       {
        case 'a, b': $final_vars='StartA, StartB'; break;
        case 'x, z': $final_vars='EndA, EndB'; break;
    }
    echo '<td style="text-align: right;"> ('.$dates_ranges.') </td>';
    if(empty($i)) { 
        echo '<td>&larr;&rarr;</td>'; 
    }
    $i=1;
}
echo '</tr></table>';

return array('a' => $a2, 'b' => $b2, 'x' => $x2, 'z' => $z2);
}

function result($result) {
if($result) {
    echo '<span style="background: green; color: white; padding: 1px 10px;">Dates match</span>';
}
else {
    echo '<span style="background: red; color: white; padding: 1px 10px;">Dates <b>NOT</b> match</span>';
}
echo '<hr />';
}

function stack_overflow_answers($case,$a,$b,$x,$z) {
#StartA -> a 
#StartB -> b
#EndA -> x
#EndB -> z
echo '<br />';
switch($case)
{
    case 'Charles Bretana - first':

        echo '<b>(StartA <= EndB) and (StartB <= EndA) and (StartA <= EndA) and (StartB <= EndB)</b><br />';
        if( ( ($a <= $z) && ($b <= $x) && ($a <= $x) && ($b <= $z) ) )
            result(false);
        else
            result(true);
        break;

    case 'Charles Bretana - second':

        echo '<b>(StartA <= EndB) and (StartA <= EndA) and (StartB <= EndA) and (StartB <= EndB)</b><br />';
        if(  ($a <= $z) && ($a <= $x) && ($b <= $x) && ($b <= $z)  )
            result(false);
        else
            result(true);
        break;

    case 'Charles Bretana - third':

        echo '<b>(StartA <= Min(EndA, EndB) and (StartB <= Min(EndA, EndB))</b> &rarr; Missing bracket?<br />';
        if(  $x <= Min($x, $z) && ( $b <= Min($x, $z) ) )
            result(false);
        else
            result(true);
        break;

    case 'Charles Bretana - fourth':

        echo '<b>(Max(StartA, StartB) <= Min(EndA, EndB)</b> &rarr; Missing bracket too?<br />';
        if(  Max($a, $b) <= Min( $x, $z) )
            result(false);
        else
            result(true);
        break;

    case 'Charles Bretana - maybe all cases in once?':

        echo '<b>(StartA <= EndB) and (StartB <= EndA) and (StartA <= EndA) and (StartB <= EndB)<br />';
        echo '(StartA <= EndB) and (StartA <= EndA) and (StartB <= EndA) and (StartB <= EndB)<br />';
        echo '(StartA <= Min(EndA, EndB) and (StartB <= Min(EndA, EndB))<br />';
        echo '(Max(StartA, StartB) <= Min(EndA, EndB)<br />';
        echo '</b><br />';
        if( 
               ( ($a <= $z) && ($b <= $x) && ($a <= $x) && ($b <= $z) ) 
            || ( ($a <= $z) && ($a <= $x) && ($b <= $x) && ($b <= $z)  )
            || ( $x <= Min($x, $z) && ( $b <= Min($x, $z) ) )
            || ( Max($a, $b) <= Min( $x, $z) )
        )
            result(false);
        else
            result(true);
        break;

    case 'Charles Bretana - using C':

        echo '<b>(StartA > StartB? Start A: StartB) <= (EndA < EndB? EndA: EndB)</b><br />';
            $result=($a > $b? $a: $b) <= ($x < $z? $x: $z);
            if($result === false)
                result( true  );
            else
                result( false );

        break;

    case 'Ian Nelson':

        echo '<b>(StartDate1 <= EndDate2) and (StartDate2 <= EndDate1)</b><br />';
        if( 
                ($a <= $z) && ($b <= $x)

        )
            result(false);
        else
            result(true);
        break;

    case 'First Good Solution? Almost':
        echo '<b>Min(StartA, StartB) >= Max(EndA, EndB) OR Max(StartA, StartB) <= Min(EndA, EndB)</b><br />';
        if( ( Min($a, $b) >= Max($x, $z) ) ||  ( Max($a, $b) <= Min($x, $z) )    
                && $a !== $x
                && $b !== $x
                && $a !== $z
                && $b !== $z
            )
            result(false);
        else
            result(true);

        break;

    case 'JustOnUnderMillions':
        echo '<b>Simplyfy function sort before</b><br />';
        $ranges = array(
                    array(array($a,$b),array($x,$z)),
            );
            foreach($ranges as $set){
                    //to change the order of the ranges for testing
                    shuffle($set);
                    //now order it
                    usort($set,function($a,$b){
                             if ($a[0] == $b[0]) { return 0; }
                             return ($a[0] < $b[0]) ? -1 : 1;
                    });
                    //test DR2S > DR1E no overlap
                    if($set[1][0] > $set[0][1]){
                            result(false);
                    } else {
                            result(true);
                    }
            }

            break;
}
}

for($i=1; $i <= 8; $i++) {
$case='Charles Bretana - first';
// $case='Charles Bretana - second';
//  $case='Charles Bretana - third';
//  $case='Charles Bretana - fourth';
//  $case='Charles Bretana - maybe all cases in once?';
//  $case='Charles Bretana - using C';
//  $case='Ian Nelson';
//  $case='First Good Solution? Almost';
//  $case='JustOnUnderMillions';


if($i === 1) { echo '<hr />Case <span style="color: blue;">'.$case.'</span><hr />'; }
echo 'Combination  <span style="color: red;">'.$i.'</span><br />';
$temp=simulate_ranges($i);
$a = $temp['a'];
$b = $temp['b'];
$x = $temp['x'];
$z = $temp['z'];
 stack_overflow_answers($case,$a,$b,$x,$z);
}

Thanks for @JustOnUnderMillions for fast and great response!

UPDATED - 2017.04.06 13:20 - added case 8 date range and @JustOnUnderMillions calculation. His case is working good in all cases.

When we put those date ranges only @JustOnUnderMillions calculate is good

    # https://stackoverflow.com/questions/43250973/two-dates-range-overlap-1501-people-missing-bug-php
$time_min='2017-01-01 01:00:00';
$time_max='2017-01-02 00:00:00';
$time_checked_min='2017-01-01 00:00:01';
$time_checked_max='2017-01-01 01:00:00';

var_dump( checkRangeBetweenRange( $time_min, $time_max, $time_checked_min, $time_checked_max ) );

function checkRangeBetweenRange( $time_min, $time_max, $time_checked_min, $time_checked_max, $convert_date=true ){
    # convert date time
    if($convert_date)   {
        $time_min=strtotime($time_min);
        $time_max=strtotime($time_max);
        $time_checked_min=strtotime($time_checked_min);
        $time_checked_max=strtotime($time_checked_max);
    }

    # https://stackoverflow.com/questions/43250973/two-dates-range-overlap-1501-people-missing-bug-php
    $ranges = array(
                array(array($time_min,$time_max),array($time_checked_min,$time_checked_max)),
        );
        foreach($ranges as $set){
                //to change the order of the ranges for testing
                shuffle($set);
                //now order it
                usort($set,function($a,$b){
                         if ($a[0] == $b[0]) { return 0; }
                         return ($a[0] < $b[0]) ? -1 : 1;
                });
                //test DR2S > DR1E no overlap
                if($set[1][0] > $set[0][1]){
                        return false;
                } else {
                        return true;
                }
        }


}
  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

1条回答

  • doutang7415 doutang7415 4年前

    I have only a Note to complexity used:

    It is all about check a daterange against daterange, so all the stuff called EndA StartA EndB is wired.

    I would first check witch date is earlier in range. And then sort it before using it, so the Charles Bretana - maybe all cases in once? is not needed.

    Just order the dateranges before check them in detail. If you have done this, one single check will say if they overlap or not.

    DR = DateRange , 1 = earlier start than 2, S = start , E = End

    DR2S > DR1E = No Overlap (here we dont do >=)

    $ranges = array(
        //only non overlap
        array(array('2017-01-01','2017-01-02'),array('2017-01-03','2017-01-04')),
        //rest overlapping
        array(array('2017-01-01','2017-01-02'),array('2017-01-02','2017-01-04')),
        array(array('2017-01-01','2017-01-02'),array('2017-01-01','2017-01-04')),
        array(array('2017-01-01','2017-01-03'),array('2017-01-03','2017-01-04')),
    );
    foreach($ranges as $set){
        //to change the order of the ranges for testing
        shuffle($set);
        //now order it
        usort($set,function($a,$b){
             if ($a[0] == $b[0]) { return 0; }
             return ($a[0] < $b[0]) ? -1 : 1;
        });
        //show 
        print implode(' - ',$set[0]).' vs '.implode(' - ',$set[1]);
        //test DR2S > DR1E no overlap
        if($set[1][0] > $set[0][1]){
            print ' NO OVERLAP<br>';
        } else {
            print ' OVERLAP<br>';
        }
    }
    

    Results:

    2017-01-01 - 2017-01-02 vs 2017-01-03 - 2017-01-04 NO OVERLAP

    2017-01-01 - 2017-01-02 vs 2017-01-02 - 2017-01-04 OVERLAP

    2017-01-01 - 2017-01-04 vs 2017-01-01 - 2017-01-02 OVERLAP

    2017-01-01 - 2017-01-03 vs 2017-01-03 - 2017-01-04 OVERLAP

    Hopefully this simplifies the topic a little bit.

    点赞 评论 复制链接分享