0

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


}
Community
  • 1
  • 1
Lesenus
  • 51
  • 1
  • 4
  • function `color()` is missing – JustOnUnderMillions Apr 06 '17 at 09:32
  • You are right I forgot to change it to html. Also I added one new case & another fix to our function in new Case 7 – Lesenus Apr 06 '17 at 09:57
  • I am having trouble understanding your question and your result, but is it correct to say you found a high-upvoted answer, and in this question you try to find out if there is a bug? It would help (me at least) great if you would have a short intro with what the wrong 'solution' was, what your input is, and what the (wrong) output was. That could be done in 3 lines. Also, can you add a not to that original answer? because nothing there indicates some sort of issue – Nanne Apr 06 '17 at 11:40
  • The point of the topic is to find good answer to all cases dates range to overlap. It is hard to explain all combination of dates that overlap or not. My cases in code showed the most common dates ranges to check. Rest of function print on the screen anserwer to all dates range and showed that all answers from http://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap have BAD solution cause show wrong answer to overlap dates in case 6. @JustOnUnderMillions gave me good answer. – Lesenus Apr 06 '17 at 11:46

1 Answers1

1

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.

Community
  • 1
  • 1
JustOnUnderMillions
  • 3,741
  • 9
  • 12
  • Thanks JustOnUnderMillions your case is great and proved that whole 9 years old topic is buged and no one saw that. And thank you for great solution. Now I know I can conticue my job with good function to dates range overlap! – Lesenus Apr 06 '17 at 10:26
  • 1
    @Lesenus Always nice to help :-) – JustOnUnderMillions Apr 06 '17 at 10:26
  • Now I know your solution is best. My calculations fail in that case: $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'; – Lesenus Apr 06 '17 at 10:57
  • @Lesenus If the date format is alway YYYY-MM-DD then work with `substr($date,0,10)` :-) I have also not used `strtotime()` (not using it will not work with format DD-MM-YYYY), so always prepare your data in the right way. And daterange checks are always simpler then datetime-range checks. – JustOnUnderMillions Apr 06 '17 at 10:58
  • I did but in the offline world :D On the bottom of my post is your case in function with strtotime ;) – Lesenus Apr 06 '17 at 11:02
  • And when we put those ranges in my function there is strtotime so it doesn't matter what format we give when we put date ranges in my functions. – Lesenus Apr 06 '17 at 11:12
  • @Lesenus Fine, was just pointing out the steps: prepare (`strtotime` or `substr` ..what is needed/better/usefull), validated (here sorting of time ranges), check (real test). – JustOnUnderMillions Apr 06 '17 at 11:15