Determining if two time ranges overlap at any point

15,332

Solution 1

The logic is correct. The timestamps you provided for $red (8-8:50pm) and $orange (1:30-3:30pm) do not overlap.

Given correct values (that reflect your screenshot), the overlap is indeed found:

function show_date($value, $key) {
    echo $key, ': ', date('r', $value), PHP_EOL;
}

$red = array('start' => strtotime('today, 2pm'), 'end' => strtotime('today, 2:45pm'));
$orange = array('start' => strtotime('today, 1:30pm'), 'end' => strtotime('today, 4pm'));

array_walk($red, 'show_date');
array_walk($orange, 'show_date');

if (($red['start'] <= $orange['end']) && ($red['end'] >= $orange['start'])) {
    echo 'Conflict handling';
}

My guess would be you have a timezone conversion issue.

Solution 2

If you have two ranges [b1, e1] and [b2, e2] (where it is already established that b1 < e1 and b2 < e2) then the overlap is detected by the following logical expression

not (e2 < b1 or e1 < b2)

which can be rewritten as

e2 >= b1 and e1 >= b2

In your syntax that would be

if(($orange['end'] >= $red['start']) && ($red['end'] >= $orange['start'])) {
   //Conflict handling
}

I.e. you got it correctly. Why you are claiming "Working through the numbers logically, I understand why the statement above fails." is not clear to me. What exactly fails? (And I don't know why is everyone coming up with ridiculously "overengineered" checks, with more than two comparisons.)

Of course, you have to decide whether touching ranges are considered overlapping and adjust the strictness of the comparisons accordingly.

P.S. The sample ranges you provided in your edit do not overlap, and your comparison correctly recognizes it as no-conflict situation. I.e. everything works as it should. Where do you see the problem?

Solution 3

You need to check if the you have a "RED" task which starts OR ends between the start and the end of an "ORANGE" task. Like this you should detect every "ORANGE" task overlapping a "RED" task.

if((($red['start'] <= $orange['end']) && ($red['start'] >= $orange['start'])) ||
   (($red['end'] <= $orange['end']) && ($red['end'] >= $orange['start'])) ) {
    //Conflict handling
}

EDIT: as stated by AndreyT this is kind of overkill and you can do better with less check

Share:
15,332

Related videos on Youtube

Michael Irigoyen
Author by

Michael Irigoyen

I am a software engineer who has been working in the industry for over fifteen years. I enjoy music, gaming, iconography, and home improvement projects. I love to continuously learn new things, and I find expanding my knowledge base often comes while helping others do the same. One of my passions is the Extra Life fundraiser, which raises money for kids in Children's Miracle Network Hospitals. Learn more about me at https://www.irigoyen.dev.

Updated on July 11, 2022

Comments

  • Michael Irigoyen
    Michael Irigoyen almost 2 years

    Possible Duplicate:
    Determine Whether Two Date Ranges Overlap

    I am trying to work out if two time ranges in PHP overlap. I've been referring to Determine Whether Two Date Ranges Overlap for my initial try, however, it's not matching all cases. If a time range is nested in between the start and end times of another time range, it's not being matched. If it overlaps the beginning or the end of the shift, or if the shifts are exact matches, it works as expected.

    Check out this image of what I'm talking about:

    enter image description here

    Basically, I am trying to hide any orange shifts if they overlap any red shifts anywhere. Here's the relevant portion of code I'm trying to use to make this happen.

    if(($red['start'] <= $orange['end']) && ($red['end'] >= $orange['start'])) {
        //Conflict handling
    }
    

    The values of the variables are UNIX timestamps. Working through the numbers logically, I understand why the statement above fails. There are obviously ways I could do more logic to determine if the one shift falls in the other shift (which is what I may need to do), but I was hoping for a more universal catch.

    EDIT: Adding the values of each block's start and end time. I agree what I have should work. The fact that it isn't is where my issue lies. I'm probably overlooking something dumb.

    orange-start = 1352899800
    orange-end = 1352907000
    
    red-start = 1352923200
    red-end = 1352926200
    

    Therefore my logic would state:

    if((1352923200 <= 1352907000) && (1352926200 >= 1352899800))
    

    So following that, the first comparison fails.

    EDIT 2: It looks like my logic is sound (which I thought was the case), and my issue is something related to the UNIX timestamp not matching the actual time being displayed. I thank those who worked though this with me and help me discover that as being the issue. I wish I could accept both Andrey's and Jason's answers.

    • Jason McCreary
      Jason McCreary over 11 years
      The logic you have should work. Can you post the values of $red and $orange for this case.
    • AnT stands with Russia
      AnT stands with Russia over 11 years
      @Michael Irigoyen: The ranges you provided as an example do not overlap. Orange ends at 1352907000, while red begins at 1352923200, which is later. There's no conflict. So the comparison fails, as it should.
  • AnT stands with Russia
    AnT stands with Russia over 11 years
    That is woefully excessive. On top of that, if you simplify this redundant check using the rules of formal logic, you will get exactly what the OP has in his version
  • koopajah
    koopajah over 11 years
    Yes you are right your answer is better explained and works perfectly. No need to use the term woefully and criticize every other answer in your own anyway :)
  • Jason McCreary
    Jason McCreary over 11 years
    The extra logic is not needed.
  • Jason McCreary
    Jason McCreary over 11 years
    The extra logic is not needed.
  • Michael Irigoyen
    Michael Irigoyen over 11 years
    I'm beginning to think you're right. I'm now looking more closely at the timestamps, and while the times show correctly, the UNIX timestamp is incorrect, thus making it look like the shifts aren't overlapping, while they really are.
  • Jason McCreary
    Jason McCreary over 11 years
    How are you doing the timestamp conversion? strtotime()?
  • Michael Irigoyen
    Michael Irigoyen over 11 years
    The one item comes in a UNIX timestamp already (from the calendar framework). The other item comes from a datetime object, and then strtotime'd. There is an inconsistency going on somewhere there. I'm sure I'll be able to track it down.