<?php
$opening_hours = [['08:00','12:00'], ['14:00','18:00']];
$occupied_slots = [['09:30','11:00'], ['15:10','16:35']];
$expected_result = [['08:00','09:30'], ['11:00','12:00'], ['14:00','15:10'], ['16:35','18:00']];
$valid_timeslots = [];
#find empty timeslots during opening hours given occupied slots
function timeToNum($time) {
return 60*$matches[1] + $matches[2];
}
function numToTime($num) {
$m = $num%60;
return ($h>9? $h:"0".$h).":".($m>9? $m:"0".$m);
}
// substraction interval $b from interval $a
function sub($a,$b)
{
// case A: $b inside $a
if($a[0]<=$b[0] and $a[1]>=$b[1]) return [ [$a[0],$b[0]], [$b[1],$a[1]] ];
// case B: $b is outside $a
if($b[1]<=$a[0] or $b[0]>=$a[1]) return [ [$a[0],$a[1]] ];
// case C: $a inside $b
if($b[0]<=$a[0] and $b[1]>=$a[1]) return [[0,0]]; // "empty interval"
// case D: left end of $b is outside $a
if($b[0]<=$a[0] and $b[1]<=$a[1]) return [[$b[1],$a[1]]];
// case E: right end of $b is outside $a
if($b[1]>=$a[1] and $b[0]>=$a[0]) return [[$a[0],$b[0]]];
}
// flat array and change numbers to time and remove empty (zero length) interwals e.g. [100,100]
// [[ [167,345] ], [ [433,644], [789,900] ]] to [ ["07:00","07:30"], ["08:00","08:30"], ["09:00","09:30"] ]
// (number values are not correct in this example)
function flatAndClean($interwals) {
$result = [];
foreach($interwals as $inter) {
foreach($inter as $i) {
if($i[0]!=$i[1]) {
//$result[] = $i;
$result[] = [numToTime($i[0]), numToTime($i[1])];
}
}
}
return $result;
}
// calculate new_opening_hours = old_opening_hours - occupied_slot
function cutOpeningHours($op_h, $occ_slot) {
foreach($op_h as $oh) {
$ohn = [timeToNum($oh[0]), timeToNum($oh[1])];
$osn = [timeToNum($occ_slot[0]), timeToNum($occ_slot[1])];
$subsn[] = sub($ohn, $osn);
}
return $subsn;
}
$oph = $opening_hours;
foreach($occupied_slots as $os) {
$oph = flatAndClean(cutOpeningHours($oph, $os ));
}
$valid_timeslots = $oph;
PD9waHAKCiRvcGVuaW5nX2hvdXJzICAgPSBbWycwODowMCcsJzEyOjAwJ10sIFsnMTQ6MDAnLCcxODowMCddXTsKJG9jY3VwaWVkX3Nsb3RzICA9IFtbJzA5OjMwJywnMTE6MDAnXSwgWycxNToxMCcsJzE2OjM1J11dOwokZXhwZWN0ZWRfcmVzdWx0ID0gW1snMDg6MDAnLCcwOTozMCddLCBbJzExOjAwJywnMTI6MDAnXSwgWycxNDowMCcsJzE1OjEwJ10sIFsnMTY6MzUnLCcxODowMCddXTsKJHZhbGlkX3RpbWVzbG90cyA9IFtdOwogCiNmaW5kIGVtcHR5IHRpbWVzbG90cyBkdXJpbmcgb3BlbmluZyBob3VycyBnaXZlbiBvY2N1cGllZCBzbG90cwogCiBmdW5jdGlvbiB0aW1lVG9OdW0oJHRpbWUpIHsKIAlwcmVnX21hdGNoKCcvKFxkXGQpOihcZFxkKS8nLCAkdGltZSwgJG1hdGNoZXMpOwogCXJldHVybiA2MCokbWF0Y2hlc1sxXSArICRtYXRjaGVzWzJdOwogfQogCiBmdW5jdGlvbiBudW1Ub1RpbWUoJG51bSkgewogCSRtICA9ICRudW0lNjA7CiAJJGggPSBpbnR2YWwoJG51bS82MCkgOwogCXJldHVybiAoJGg+OT8gJGg6IjAiLiRoKS4iOiIuKCRtPjk/ICRtOiIwIi4kbSk7CiAJCiB9CiAKIC8vIHN1YnN0cmFjdGlvbiBpbnRlcnZhbCAkYiBmcm9tIGludGVydmFsICRhCiBmdW5jdGlvbiBzdWIoJGEsJGIpIAogewogICAgIC8vIGNhc2UgQTogJGIgaW5zaWRlICRhCiAgICAgaWYoJGFbMF08PSRiWzBdIGFuZCAkYVsxXT49JGJbMV0pIHJldHVybiBbIFskYVswXSwkYlswXV0sIFskYlsxXSwkYVsxXV0gXTsKICAgICAKICAgICAvLyBjYXNlIEI6ICRiIGlzIG91dHNpZGUgJGEKICAgICBpZigkYlsxXTw9JGFbMF0gb3IgJGJbMF0+PSRhWzFdKSByZXR1cm4gWyBbJGFbMF0sJGFbMV1dIF07CiAgICAgCiAgICAgLy8gY2FzZSBDOiAkYSBpbnNpZGUgJGIKICAgICBpZigkYlswXTw9JGFbMF0gYW5kICRiWzFdPj0kYVsxXSkgcmV0dXJuIFtbMCwwXV07IC8vICJlbXB0eSBpbnRlcnZhbCIKICAgICAKICAgICAvLyBjYXNlIEQ6IGxlZnQgZW5kIG9mICRiIGlzIG91dHNpZGUgJGEKICAgICBpZigkYlswXTw9JGFbMF0gYW5kICRiWzFdPD0kYVsxXSkgcmV0dXJuIFtbJGJbMV0sJGFbMV1dXTsKICAgICAKICAgICAvLyBjYXNlIEU6IHJpZ2h0IGVuZCBvZiAkYiBpcyBvdXRzaWRlICRhCiAgICAgaWYoJGJbMV0+PSRhWzFdIGFuZCAkYlswXT49JGFbMF0pIHJldHVybiBbWyRhWzBdLCRiWzBdXV07CiB9CiAKIC8vIGZsYXQgYXJyYXkgYW5kIGNoYW5nZSBudW1iZXJzIHRvIHRpbWUgYW5kIHJlbW92ZSBlbXB0eSAoemVybyBsZW5ndGgpIGludGVyd2FscyBlLmcuIFsxMDAsMTAwXQogLy8gW1sgWzE2NywzNDVdIF0sIFsgWzQzMyw2NDRdLCBbNzg5LDkwMF0gXV0gdG8gWyBbIjA3OjAwIiwiMDc6MzAiXSwgWyIwODowMCIsIjA4OjMwIl0sIFsiMDk6MDAiLCIwOTozMCJdIF0gCiAvLyAobnVtYmVyIHZhbHVlcyBhcmUgbm90IGNvcnJlY3QgaW4gdGhpcyBleGFtcGxlKQogZnVuY3Rpb24gZmxhdEFuZENsZWFuKCRpbnRlcndhbHMpIHsKICAgICAkcmVzdWx0ID0gW107CiAgICAgZm9yZWFjaCgkaW50ZXJ3YWxzIGFzICRpbnRlcikgewogICAgICAgICBmb3JlYWNoKCRpbnRlciBhcyAkaSkgewogICAgICAgICAgICAgaWYoJGlbMF0hPSRpWzFdKSB7CiAgICAgICAgICAgICAgICAgLy8kcmVzdWx0W10gPSAkaTsKICAgICAgICAgICAgICAgICAkcmVzdWx0W10gPSBbbnVtVG9UaW1lKCRpWzBdKSwgbnVtVG9UaW1lKCRpWzFdKV07CiAgICAgICAgICAgICB9CiAgICAgICAgIH0KICAgICB9CiAgICAgcmV0dXJuICRyZXN1bHQ7CiB9CiAKIC8vIGNhbGN1bGF0ZSBuZXdfb3BlbmluZ19ob3VycyA9IG9sZF9vcGVuaW5nX2hvdXJzIC0gb2NjdXBpZWRfc2xvdAogZnVuY3Rpb24gY3V0T3BlbmluZ0hvdXJzKCRvcF9oLCAkb2NjX3Nsb3QpIHsKICAgIGZvcmVhY2goJG9wX2ggYXMgJG9oKSB7CiAgICAgCSRvaG4gPSBbdGltZVRvTnVtKCRvaFswXSksIHRpbWVUb051bSgkb2hbMV0pXTsKICAgICAJJG9zbiA9IFt0aW1lVG9OdW0oJG9jY19zbG90WzBdKSwgdGltZVRvTnVtKCRvY2Nfc2xvdFsxXSldOwogICAgIAkkc3Vic25bXSA9IHN1Yigkb2huLCAkb3NuKTsKICAgIH0KICAgIHJldHVybiAkc3Vic247CiB9CiAKIAogJG9waCA9ICRvcGVuaW5nX2hvdXJzOwogZm9yZWFjaCgkb2NjdXBpZWRfc2xvdHMgYXMgJG9zKSB7CiAgICAgJG9waCA9IGZsYXRBbmRDbGVhbihjdXRPcGVuaW5nSG91cnMoJG9waCwgJG9zICkpOwogfQogCiAkdmFsaWRfdGltZXNsb3RzID0gJG9waDsKIAogdmFyX2R1bXAoanNvbl9lbmNvZGUoWyJyZXN1bHQiPT4kdmFsaWRfdGltZXNsb3RzXSkpOwog