champton
champton

Reputation: 83

Check for overlapping times on the same date in an array?

I have an array that is produced from people wanting to reserve a time block to volunteer with our organization. I want to check to see if they chose time blocks on the same day that overlap. In my example array below the first and third elements are overlapping and I need to detect that. Any recommendation would be much appreciated:

 Array
(
    [0] => Array
        (
            [id_pc_time_blocks] => 3
            [id_pc] => 2
            [pc_date] => 2012-11-21
            [pc_time_block] => 9:00 AM-1:00 PM
            [pc_time_block_max] => 25
            [pc_time_block_count] => 0
            [pc_name] => Atlanta
        )

    [1] => Array
        (
            [id_pc_time_blocks] => 4
            [id_pc] => 2
            [pc_date] => 2012-11-21
            [pc_time_block] => 1:00 PM-5:00 PM
            [pc_time_block_max] => 25
            [pc_time_block_count] => 10
            [pc_name] => Atlanta
        )

    [2] => Array
        (
            [id_pc_time_blocks] => 6
            [id_pc] => 2
            [pc_date] => 2012-11-21
            [pc_time_block] => 10:00 AM-2:00 PM
            [pc_time_block_max] => 25
            [pc_time_block_count] => 0
            [pc_name] => Atlanta
        )

       [3] => Array
        (
            [id_pc_time_blocks] => 6
            [id_pc] => 2
            [pc_date] => 2012-11-23
            [pc_time_block] => 10:00 AM-2:00 PM
            [pc_time_block_max] => 25
            [pc_time_block_count] => 0
            [pc_name] => Atlanta
        )

       [4] => Array
        (
            [id_pc_time_blocks] => 6
            [id_pc] => 2
            [pc_date] => 2012-11-23
            [pc_time_block] => 3:00 AM-6:00 PM
            [pc_time_block_max] => 25
            [pc_time_block_count] => 0
            [pc_name] => Atlanta
        )

)

Upvotes: 0

Views: 1436

Answers (3)

Hard-Boiled Wonderland
Hard-Boiled Wonderland

Reputation: 1399

Robbie's answer did not work for me. I found the rules that needed to be in place going off his example for just dates were:

$i[start] needs to be less than and not equal to $i[stop]

$i[start] needs to greater than and not equal to $j[stop]

$j[start] needs to be less than and not equal to $j[stop]

Therefore my solution was:

$numElements = count($dates);

for ($i=0; $i<$numElements; $i++) {

    $dates[$i]['start_time'] = strtotime($dates[$i]['start']);
    $dates[$i]['end_time'] = strtotime($dates[$i]['end']);

    if ($i > 0) {

        for ($j=0; $j<$i;$j++) {

            if($dates[$i]['start_time'] >= $dates[$i]['end_time'] || $dates[$i]['start_time'] <= $dates[$j]['end_time'] || $dates[$j]['start_time'] >= $dates[$j]['end_time']) {
                $this->set_error(['dates_overlap']);
                $this->dates_overlap = true;
                break;
            }

        }
    }

    if(isset($this->dates_overlap))
        break;
}

Upvotes: 0

champton
champton

Reputation: 83

Here is my solution for checking each date for overlapping times. The only thing is, this is for my particular scenario and does not account for overlapping years as I dont have that need for this application.

Here is my working example:

$dateIdx = 0;
    foreach($timeblocks_array as $obj) {
           $timeblocks_array[$dateIdx]["intDay"] = idate("z",strtotime($obj["pc_date"]));
           $timeblocks_array[$dateIdx]["intStart"] = intval($obj["start_time"]);
           $timeblocks_array[$dateIdx]["intEnd"] = intval($obj["end_time"]);
           $mindates[] = idate("z",strtotime($obj["pc_date"]));
           $dateIdx++;
    }
    $minDateSingle = min($mindates);
    $maxDateSingle = max($mindates);

    $currentDate = $minDateSingle;
    $dateIdx = 0;
    while ($currentDate <= $maxDateSingle) {
           $hrIndex = 0;
           while ($hrIndex < 24) {
                  $matrixArray[$dateIdx][$hrIndex]["count"] = 0;
                  $matrixArray[$dateIdx][$hrIndex]["intDay"] = $currentDate;
                  $hrIndex++;         
           }
           // calculate counts:
           $hourIdx = 0;
           foreach($matrixArray[$dateIdx] as $hour){
                  foreach($timeblocks_array as $block) {
                         if ($hour["intDay"] == $block["intDay"]) {
                               if ($hourIdx >= $block["intStart"] && $hourIdx < $block["intEnd"]) {
                                      $matrixArray[$dateIdx][$hourIdx]["count"] = $matrixArray[$dateIdx][$hourIdx]["count"] + 1;
                                      $matrixArray[$dateIdx][$hourIdx]["requests"][] = $block;
                               }
                         }
                  }
                  $hourIdx++;
           }
           $dateIdx++;
           $currentDate = $currentDate + 1; 
    }

    //loop through the matrix array and timeblocks array to see if they intersect
    foreach($matrixArray as $day) {
           $hourIdx = 0;
           foreach($day as $hour) {
                  if ($hour["count"] > 1) {
                         //echo $hour["intDay"]." - Overlap on Hour $hourIdx\n";
                         $smarty->assign('overlappingError', 1);
                         $error = 1;
                         foreach($hour["requests"] as $overlapblock) {
                               //echo " --> conflict: ". $overlapblock["pc_date"]." ".$overlapblock["pc_time_block"]." (".$overlapblock["intStart"]." to ".$overlapblock["intEnd"].")\n";
                         }
                  } else if ($hour["count"] == 1) {
                         // these are valid hours
                  }     
                  $hourIdx++;
           }     
    }   

Upvotes: 0

Robbie
Robbie

Reputation: 17720

Not recommended for HUGE arrays, but here's a quick solution. You need to break hte times into unix time stamps for comparisson

// Run down each element of the array. (I've called it MyStartArray)
$numElements = count($MyStartArray);
for ($i=0; $i<$numElements ; $i++) {

    // Calculate "Start Time" and "End Time" as Unix time stamps (use mktime function) and store as another items in the array
    // You can use preg_match or substr to get the values to pump into mktime() below - not writing hte whole thing for you ;)
    $MyStartArray[$i]['start_time'] = mktime( ... );
    $MyStartArray[$i]['end_time'] = mktime( ... );

    // Now run through all the previous elements to see if a start time is before the end time, or an end time is after the start time.
    if ($i > 0) {
        for ($j=0; $j<$i;$j++) {
            if ($MyStartArray[$i]['start_time'] < $MyStartArray[$j]['end_time'] ||
                  $MyStartArray[$j]['end_time'] > $MyStartArray[$j]['start_time'] ) {
                     echo 'CLASH';
            }
        }
    }
}

Upvotes: 1

Related Questions