Search code examples
phpdatetimeconflict

How to check for conflict with multiple datetime RANGES


I am stumped about how to go about checking to see if multiple *date/time value -ranges- (meaning: start and end times) are in conflict with one another.

Summary & samples of data I need to work with.

I have an (app) events table, that is used to create a form for the user to go through and check off (checkboxes) all the sessions/events they attended.

The names of the checkboxes are generated using info about the session. (month/day start/end times along with a session id)

ie: hours_9_5_p02_800_845

9 = month
5 = date
p02 = session id/event code
800 = start time
845 = end time

all delimited by an underscore ("_")

If the user checks (and submits) multiple sessions, there will be many of these values to check for a time conflict..

hours_9_5_p08_755_800
hours_9_5_p02_800_845
hours_9_5_p02_800_855
hours_9_5_p03_800_845
hours_9_5_p04_820_835
hours_9_5_p04_845_900
hours_9_5_REG_900_915
hours_9_5_REG_1300_1305
hours_9_5_REG_1310_1335
hours_9_5_REG_1320_1335

The above is an example of the fieldlist/array that I 'could' get as a user selection/submission that I need to check for any possible conflicts (obviously the user couldnt be two places at once) :) And the above have many/several overlapping of just the same exact time slots selected.

** I am open to either PHP, (checking after the user submits) or javascript/jQuery (if it can do the date/time RANGE conflict checking, it might be easier to then highlight those rows/elements on the page if done on the front end)

I'd image, first you need to parse those checkbox names/strings from the fieldlist array...

which I have done like so: (php)

function conflictParse($delimiter, $targetString){
    //echo 'fired';
    $breakDown = explode($delimiter, $targetString);
    $startTime = substr_replace($breakDown[4] , ':', -2, 0);
    $endTime = substr_replace($breakDown[5] , ':', -2, 0);
    $startString = "$breakDown[1]/$breakDown[2]/2015 $startTime";
    $endString = "$breakDown[1]/$breakDown[2]/2015 $endTime";
    $startFormat = strtotime($startString);
    $endFormat = strtotime($endString);
    $start = date('m/d/Y G:i',$startFormat);
    $end = date('m/d/Y G:i',$endFormat);

    return "Session Times: $start -- $end <br>";
}
echo conflictParse('_','hours_9_5_p02_800_845');

but I am not clear on HOW to go about using this RANGE of a date start & end time to check against MULTIPLE other date start/end time RANGES?

maybe just sticking with having PHP parse/check conflict upon submit and then return some array of the (original) names page to the page (for some jQuery to use and highlight the elements..etc (but I can handle that aspect later.. for right now I am need help on how I can get the above parse 'date/time' start/end range values checked for conflicts against other 'date/time' start/end range values

update:

Here is the current nested associative array I have to work with for comparing:

Array ( 
    [0] => Array ( 
        [id] => hours_9_9_p02_800_845 
        [fullStart] => 09/09/2015 8:00 
        [fullEnd] => 09/09/2015 8:45 
        [month] => 9 
        [date] => 9 
        [session_code] => p02 
        [start] => 8:00 
        [end] => 8:45 
        [hasConflict] => false 
    ) 
    [1] => Array ( 
        [id] => hours_9_9_p02_800_855 
        [fullStart] => 09/09/2015 8:00 
        [fullEnd] => 09/09/2015 8:55 
        [month] => 9 
        [date] => 9 
        [session_code] => p02 
        [start] => 8:00 
        [end] => 8:55 
        [hasConflict] => false 
    ) 
    [2] => Array ( 
        [id] => hours_9_9_p03_800_845 
        [fullStart] => 09/09/2015 8:00 
        [fullEnd] => 09/09/2015 8:45 
        [month] => 9 
        [date] => 9 
        [session_code] => p03 
        [start] => 8:00 
        [end] => 8:45 
        [hasConflict] => false 
    ) 
    [3] => Array ( 
        [id] => hours_9_9_p04_820_830 
        [fullStart] => 09/09/2015 8:20 
        [fullEnd] => 09/09/2015 8:30 
        [month] => 9 
        [date] => 9 
        [session_code] => p04 
        [start] => 8:20 
        [end] => 8:30 
        [hasConflict] => false 
    ) 
    [4] => Array ( 
        [id] => hours_9_9_p04_845_900 
        [fullStart] => 09/09/2015 8:45 
        [fullEnd] => 09/09/2015 9:00 
        [month] => 9 
        [date] => 9 
        [session_code] => p04 
        [start] => 8:45 
        [end] => 9:00 
        [hasConflict] => false 
    ) 
    [5] => Array ( 
        [id] => hours_9_9_REG_1300_1315 
        [fullStart] => 09/09/2015 13:00 
        [fullEnd] => 09/09/2015 13:15 
        [month] => 9 
        [date] => 9 
        [session_code] => REG 
        [start] => 13:00 
        [end] => 13:15 
        [hasConflict] => false 
    ) 
    [6] => Array ( 
        [id] => hours_9_9_REG_1300_1330 
        [fullStart] => 09/09/2015 13:00 
        [fullEnd] => 09/09/2015 13:30 
        [month] => 9 
        [date] => 9 
        [session_code] => REG 
        [start] => 13:00 
        [end] => 13:30 
        [hasConflict] => false 
    ) 
) 

I need to convert your js functions over to PHP and of course use the fullStart/fullEnd variables in my time compares I guess..??

(but your function is still confusing me as I see references to event1, event 2.. (to match your example)..

update 2:

The above is my object/array (associative array) that I got from selecting some check boxes, and submitting my form...

Here is my attempt to convert your JS code to PHP based [with some update variablenames]: (and the commented out lines just to try and get some sort of output somewhere)

print_r($conflict_list);

    function checkFirst($cf_presX, $cf_presY) {
        //$cf_presX['fullStart'] < $cf_presY['fallStart'] ? checkConflict($cf_presX, $cf_presY) : checkConflict($cf_presY, $cf_presX);

        echo 'Pres Check: '.$cf_presX[0] . '<br>';
        echo 'Pres Check: '.$cf_presY[0] . '<br>';
        /*
        function checkConflict ($cc_presX, $cc_presY) {      
            if ($cc_presX.['fullEnd'] > $cc_presY.['fullStart']) {
                $cc_presX.['hasConflict'] = true;
                $cc_presY.['hasConflict'] = true;
            }
        }
        */
    }

    function setConflicts($events) {
        for ($i = 0; $i < count($events); $i++) {
            for ($j = 0; $i < count($events); $j++) {
              // if it is not the same event
              // if (i !== j) is the same $age['Peter']
              if ($events[$i]['id'] !== $events[$j]['id']) {
                checkFirst($events[$i], $events[$j]);
              }
            }
        }
    }
    setConflicts($conflict_list);

I just keep getting a loop with undefined offset: (counting up to the 100k+ mark)

Notice: Undefined offset: 0 in C:\wamp\www\projects\misc\conflict_check_new.php on line 49 Pres Check:

Notice: Undefined offset: 0 in C:\wamp\www\projects\misc\conflict_check_new.php on line 50 Pres Check:

Notice: Undefined offset: 0 in C:\wamp\www\projects\misc\conflict_check_new.php on line 49 Pres Check:


Solution

  • The same logic could apply in PHP, but assuming you can get your events out into JavaScript and create an array of objects with start and end dates like so:

    var events = [
      {
        id: 'event1',
        start: new Date('1/1/1 5:00'),
        end: new Date('1/1/1 6:00'),
        hasConflict: false
      },
      {
        id: 'event2',
        start: new Date('1/1/1 5:30'),
        end: new Date('1/1/1 6:30'),
        hasConflict: false
      },
      {
        id: 'event3',
        start: new Date('1/1/1 7:30'),
        end: new Date('1/1/1 8:30'),
        hasConflict: false
      }
    ]
    

    You can compare events to see if the one that starts first, has an end time that's later the second one's start time.

    function checkFirst (event1, event2) {
      event1.start < event2.start 
        ? checkConflict(event1, event2) 
        : checkConflict(event2, event1)
    
      function checkConflict (first, second) {      
        if (first.end > second.start) {
          first.hasConflict = second.hasConflict = true
        }
      }
    }
    

    Then you can check events against each other. Here's a not particularly efficient, but at least suitable loop:

    function flagAllEventsWithConflicts (events) {
      events.forEach(event1 => {
        events.forEach(event2 => {
          event1.id !== event2.id && checkFirst(event1, event2)
        })
      })
    }
    

    Update: The above function can also be written as a nested for loop:

    function flagAllEventsWithConflicts (events) {
      for (var i = 0; i < events.length; i++) {
        for (var j = 0; j < events.length; j++ {
          // if it is not the same event
          // if (i !== j) is the same
          if (events[i].id !== events[j].id) {
            checkFirst(events[i], events[j])
          }
        }
      }
    }
    

    Then check to see if hasConflict is true or false:

    flagAllEventsWithConflicts(events)
    console.table(events)
    

    Run this fiddle and checkout the console