Delete timeslots from time range in php












0















I was trying to display free timeslots.



Given the data below, basically we need to find a way to display not booked timeslots during opening hours. Seems very easy to do as a human, but programming it...honestly I'm just going crazy :D



// open hours   --------++++--++++-----  [[08:00,12:00], [14:00,18:00]]  
// booked slots ---------++----+------- [[09:00,11:00], [15:00,16:00]]
// expected --------+--+--+-++----- [[08,09], [11,12], [14,15], [16,18]]


Just for clarity I omitted minutes, which in real program will be present.



I have prepared a fiddle to start with: https://ideone.com/Z9pPi3



<?php     
$opening_hours = [['08:00','12:00'], ['14:00','18:00']];
$occupied_slots = [['09:30','14: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 = ;

# - - - - - - - - helper functions

function timestring_to_time($hh_mm) {
return (int) strtotime("1970-01-01 $hh_mm");
}

function timestring_diff($hh_mm_start, $hh_mm_end) {
return abs(timestring_to_time($hh_mm_end) - timestring_to_time($hh_mm_start));
}

# find empty timeslots during opening hours given occupied slots
# H E R E G O E S T H E M A G I C

var_dump($valid_timeslots);


I tried to solve with if/else method but it's not working actually...there is a need of some kind recursion function.










share|improve this question

























  • Please paste all code into the question itself instead of off-site links. If that link changes/gets removed, the question will be useless for future visitors.

    – Magnus Eriksson
    Nov 16 '18 at 23:34













  • @MagnusEriksson done! Thanks for suggestion

    – KeySee
    Nov 16 '18 at 23:37











  • Have you actually tried anything? All I see is some arrays but no attempt at solving it? Also, are these values coming from a database?

    – Magnus Eriksson
    Nov 16 '18 at 23:39













  • Oh yeah, i created different monsters in order to solve this. None were good enought. The data are coming from plain text file. No database involved.

    – KeySee
    Nov 16 '18 at 23:42













  • @KeySee Btw, strtotime('1970-01-01') will always return 0, since 1970-01-01 is epoch.

    – Davіd
    Nov 17 '18 at 0:16
















0















I was trying to display free timeslots.



Given the data below, basically we need to find a way to display not booked timeslots during opening hours. Seems very easy to do as a human, but programming it...honestly I'm just going crazy :D



// open hours   --------++++--++++-----  [[08:00,12:00], [14:00,18:00]]  
// booked slots ---------++----+------- [[09:00,11:00], [15:00,16:00]]
// expected --------+--+--+-++----- [[08,09], [11,12], [14,15], [16,18]]


Just for clarity I omitted minutes, which in real program will be present.



I have prepared a fiddle to start with: https://ideone.com/Z9pPi3



<?php     
$opening_hours = [['08:00','12:00'], ['14:00','18:00']];
$occupied_slots = [['09:30','14: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 = ;

# - - - - - - - - helper functions

function timestring_to_time($hh_mm) {
return (int) strtotime("1970-01-01 $hh_mm");
}

function timestring_diff($hh_mm_start, $hh_mm_end) {
return abs(timestring_to_time($hh_mm_end) - timestring_to_time($hh_mm_start));
}

# find empty timeslots during opening hours given occupied slots
# H E R E G O E S T H E M A G I C

var_dump($valid_timeslots);


I tried to solve with if/else method but it's not working actually...there is a need of some kind recursion function.










share|improve this question

























  • Please paste all code into the question itself instead of off-site links. If that link changes/gets removed, the question will be useless for future visitors.

    – Magnus Eriksson
    Nov 16 '18 at 23:34













  • @MagnusEriksson done! Thanks for suggestion

    – KeySee
    Nov 16 '18 at 23:37











  • Have you actually tried anything? All I see is some arrays but no attempt at solving it? Also, are these values coming from a database?

    – Magnus Eriksson
    Nov 16 '18 at 23:39













  • Oh yeah, i created different monsters in order to solve this. None were good enought. The data are coming from plain text file. No database involved.

    – KeySee
    Nov 16 '18 at 23:42













  • @KeySee Btw, strtotime('1970-01-01') will always return 0, since 1970-01-01 is epoch.

    – Davіd
    Nov 17 '18 at 0:16














0












0








0


2






I was trying to display free timeslots.



Given the data below, basically we need to find a way to display not booked timeslots during opening hours. Seems very easy to do as a human, but programming it...honestly I'm just going crazy :D



// open hours   --------++++--++++-----  [[08:00,12:00], [14:00,18:00]]  
// booked slots ---------++----+------- [[09:00,11:00], [15:00,16:00]]
// expected --------+--+--+-++----- [[08,09], [11,12], [14,15], [16,18]]


Just for clarity I omitted minutes, which in real program will be present.



I have prepared a fiddle to start with: https://ideone.com/Z9pPi3



<?php     
$opening_hours = [['08:00','12:00'], ['14:00','18:00']];
$occupied_slots = [['09:30','14: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 = ;

# - - - - - - - - helper functions

function timestring_to_time($hh_mm) {
return (int) strtotime("1970-01-01 $hh_mm");
}

function timestring_diff($hh_mm_start, $hh_mm_end) {
return abs(timestring_to_time($hh_mm_end) - timestring_to_time($hh_mm_start));
}

# find empty timeslots during opening hours given occupied slots
# H E R E G O E S T H E M A G I C

var_dump($valid_timeslots);


I tried to solve with if/else method but it's not working actually...there is a need of some kind recursion function.










share|improve this question
















I was trying to display free timeslots.



Given the data below, basically we need to find a way to display not booked timeslots during opening hours. Seems very easy to do as a human, but programming it...honestly I'm just going crazy :D



// open hours   --------++++--++++-----  [[08:00,12:00], [14:00,18:00]]  
// booked slots ---------++----+------- [[09:00,11:00], [15:00,16:00]]
// expected --------+--+--+-++----- [[08,09], [11,12], [14,15], [16,18]]


Just for clarity I omitted minutes, which in real program will be present.



I have prepared a fiddle to start with: https://ideone.com/Z9pPi3



<?php     
$opening_hours = [['08:00','12:00'], ['14:00','18:00']];
$occupied_slots = [['09:30','14: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 = ;

# - - - - - - - - helper functions

function timestring_to_time($hh_mm) {
return (int) strtotime("1970-01-01 $hh_mm");
}

function timestring_diff($hh_mm_start, $hh_mm_end) {
return abs(timestring_to_time($hh_mm_end) - timestring_to_time($hh_mm_start));
}

# find empty timeslots during opening hours given occupied slots
# H E R E G O E S T H E M A G I C

var_dump($valid_timeslots);


I tried to solve with if/else method but it's not working actually...there is a need of some kind recursion function.







php loops date datetime






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 17 '18 at 1:14







KeySee

















asked Nov 16 '18 at 23:29









KeySeeKeySee

607717




607717













  • Please paste all code into the question itself instead of off-site links. If that link changes/gets removed, the question will be useless for future visitors.

    – Magnus Eriksson
    Nov 16 '18 at 23:34













  • @MagnusEriksson done! Thanks for suggestion

    – KeySee
    Nov 16 '18 at 23:37











  • Have you actually tried anything? All I see is some arrays but no attempt at solving it? Also, are these values coming from a database?

    – Magnus Eriksson
    Nov 16 '18 at 23:39













  • Oh yeah, i created different monsters in order to solve this. None were good enought. The data are coming from plain text file. No database involved.

    – KeySee
    Nov 16 '18 at 23:42













  • @KeySee Btw, strtotime('1970-01-01') will always return 0, since 1970-01-01 is epoch.

    – Davіd
    Nov 17 '18 at 0:16



















  • Please paste all code into the question itself instead of off-site links. If that link changes/gets removed, the question will be useless for future visitors.

    – Magnus Eriksson
    Nov 16 '18 at 23:34













  • @MagnusEriksson done! Thanks for suggestion

    – KeySee
    Nov 16 '18 at 23:37











  • Have you actually tried anything? All I see is some arrays but no attempt at solving it? Also, are these values coming from a database?

    – Magnus Eriksson
    Nov 16 '18 at 23:39













  • Oh yeah, i created different monsters in order to solve this. None were good enought. The data are coming from plain text file. No database involved.

    – KeySee
    Nov 16 '18 at 23:42













  • @KeySee Btw, strtotime('1970-01-01') will always return 0, since 1970-01-01 is epoch.

    – Davіd
    Nov 17 '18 at 0:16

















Please paste all code into the question itself instead of off-site links. If that link changes/gets removed, the question will be useless for future visitors.

– Magnus Eriksson
Nov 16 '18 at 23:34







Please paste all code into the question itself instead of off-site links. If that link changes/gets removed, the question will be useless for future visitors.

– Magnus Eriksson
Nov 16 '18 at 23:34















@MagnusEriksson done! Thanks for suggestion

– KeySee
Nov 16 '18 at 23:37





@MagnusEriksson done! Thanks for suggestion

– KeySee
Nov 16 '18 at 23:37













Have you actually tried anything? All I see is some arrays but no attempt at solving it? Also, are these values coming from a database?

– Magnus Eriksson
Nov 16 '18 at 23:39







Have you actually tried anything? All I see is some arrays but no attempt at solving it? Also, are these values coming from a database?

– Magnus Eriksson
Nov 16 '18 at 23:39















Oh yeah, i created different monsters in order to solve this. None were good enought. The data are coming from plain text file. No database involved.

– KeySee
Nov 16 '18 at 23:42







Oh yeah, i created different monsters in order to solve this. None were good enought. The data are coming from plain text file. No database involved.

– KeySee
Nov 16 '18 at 23:42















@KeySee Btw, strtotime('1970-01-01') will always return 0, since 1970-01-01 is epoch.

– Davіd
Nov 17 '18 at 0:16





@KeySee Btw, strtotime('1970-01-01') will always return 0, since 1970-01-01 is epoch.

– Davіd
Nov 17 '18 at 0:16












1 Answer
1






active

oldest

votes


















1














Here is my solution - I assume that first hour in time-interval like ['08:00','12:00'] is always smaller than second hour. Instead of using your timestring_to_time and timestring_diff I write my own procedures to convert time to number - timeToNum and numToTime (you can easily extend them to include seconds: num=3600*hour + 60*min + sec, sec=num%60, h=floor(num/3600), min=floor((num-h*3600)/60) ):



<?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) {
preg_match('/(dd):(dd)/', $time, $matches);
return 60*$matches[1] + $matches[2];
}

function numToTime($num) {
$m = $num%60;
$h = intval($num/60) ;
return ($h>9? $h:"0".$h).":".($m>9? $m:"0".$m);

}

// substraction interval $b=[b0,b1] from interval $a=[a0,a1]
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;

var_dump(json_encode(["result"=>$valid_timeslots]));


Working example HERE.



Algorithm



Calculate $new_opening_hours from $old_opening_hours by substract one occupied slot from it. And repeat that operation for every slot (each time getting new oppening hours array)



To make substraction of two interwals I :




  1. convert time like "08:30" to number 08*60+30 = 510

  2. Solve problem of substract two interwals [a0,a1]-[b0,b1] which can have 5 cases (look on sub function) - as example [500,800]-[600,700] = [ [500,600], [700,800]],

  3. for each opening hours substract given occupied_slot and then clean and flat result to calc NEW openning hours


You can improve a little this solution by not conver time-number on each iteration but do it for each input data at the beginning and conver number-time for each output data at the end. And probably you can reduce number of conditions in sub function - however current version is very clear.






share|improve this answer


























  • Works great. Thanks Kamil !

    – KeySee
    Nov 17 '18 at 13:20











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53346699%2fdelete-timeslots-from-time-range-in-php%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









1














Here is my solution - I assume that first hour in time-interval like ['08:00','12:00'] is always smaller than second hour. Instead of using your timestring_to_time and timestring_diff I write my own procedures to convert time to number - timeToNum and numToTime (you can easily extend them to include seconds: num=3600*hour + 60*min + sec, sec=num%60, h=floor(num/3600), min=floor((num-h*3600)/60) ):



<?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) {
preg_match('/(dd):(dd)/', $time, $matches);
return 60*$matches[1] + $matches[2];
}

function numToTime($num) {
$m = $num%60;
$h = intval($num/60) ;
return ($h>9? $h:"0".$h).":".($m>9? $m:"0".$m);

}

// substraction interval $b=[b0,b1] from interval $a=[a0,a1]
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;

var_dump(json_encode(["result"=>$valid_timeslots]));


Working example HERE.



Algorithm



Calculate $new_opening_hours from $old_opening_hours by substract one occupied slot from it. And repeat that operation for every slot (each time getting new oppening hours array)



To make substraction of two interwals I :




  1. convert time like "08:30" to number 08*60+30 = 510

  2. Solve problem of substract two interwals [a0,a1]-[b0,b1] which can have 5 cases (look on sub function) - as example [500,800]-[600,700] = [ [500,600], [700,800]],

  3. for each opening hours substract given occupied_slot and then clean and flat result to calc NEW openning hours


You can improve a little this solution by not conver time-number on each iteration but do it for each input data at the beginning and conver number-time for each output data at the end. And probably you can reduce number of conditions in sub function - however current version is very clear.






share|improve this answer


























  • Works great. Thanks Kamil !

    – KeySee
    Nov 17 '18 at 13:20
















1














Here is my solution - I assume that first hour in time-interval like ['08:00','12:00'] is always smaller than second hour. Instead of using your timestring_to_time and timestring_diff I write my own procedures to convert time to number - timeToNum and numToTime (you can easily extend them to include seconds: num=3600*hour + 60*min + sec, sec=num%60, h=floor(num/3600), min=floor((num-h*3600)/60) ):



<?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) {
preg_match('/(dd):(dd)/', $time, $matches);
return 60*$matches[1] + $matches[2];
}

function numToTime($num) {
$m = $num%60;
$h = intval($num/60) ;
return ($h>9? $h:"0".$h).":".($m>9? $m:"0".$m);

}

// substraction interval $b=[b0,b1] from interval $a=[a0,a1]
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;

var_dump(json_encode(["result"=>$valid_timeslots]));


Working example HERE.



Algorithm



Calculate $new_opening_hours from $old_opening_hours by substract one occupied slot from it. And repeat that operation for every slot (each time getting new oppening hours array)



To make substraction of two interwals I :




  1. convert time like "08:30" to number 08*60+30 = 510

  2. Solve problem of substract two interwals [a0,a1]-[b0,b1] which can have 5 cases (look on sub function) - as example [500,800]-[600,700] = [ [500,600], [700,800]],

  3. for each opening hours substract given occupied_slot and then clean and flat result to calc NEW openning hours


You can improve a little this solution by not conver time-number on each iteration but do it for each input data at the beginning and conver number-time for each output data at the end. And probably you can reduce number of conditions in sub function - however current version is very clear.






share|improve this answer


























  • Works great. Thanks Kamil !

    – KeySee
    Nov 17 '18 at 13:20














1












1








1







Here is my solution - I assume that first hour in time-interval like ['08:00','12:00'] is always smaller than second hour. Instead of using your timestring_to_time and timestring_diff I write my own procedures to convert time to number - timeToNum and numToTime (you can easily extend them to include seconds: num=3600*hour + 60*min + sec, sec=num%60, h=floor(num/3600), min=floor((num-h*3600)/60) ):



<?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) {
preg_match('/(dd):(dd)/', $time, $matches);
return 60*$matches[1] + $matches[2];
}

function numToTime($num) {
$m = $num%60;
$h = intval($num/60) ;
return ($h>9? $h:"0".$h).":".($m>9? $m:"0".$m);

}

// substraction interval $b=[b0,b1] from interval $a=[a0,a1]
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;

var_dump(json_encode(["result"=>$valid_timeslots]));


Working example HERE.



Algorithm



Calculate $new_opening_hours from $old_opening_hours by substract one occupied slot from it. And repeat that operation for every slot (each time getting new oppening hours array)



To make substraction of two interwals I :




  1. convert time like "08:30" to number 08*60+30 = 510

  2. Solve problem of substract two interwals [a0,a1]-[b0,b1] which can have 5 cases (look on sub function) - as example [500,800]-[600,700] = [ [500,600], [700,800]],

  3. for each opening hours substract given occupied_slot and then clean and flat result to calc NEW openning hours


You can improve a little this solution by not conver time-number on each iteration but do it for each input data at the beginning and conver number-time for each output data at the end. And probably you can reduce number of conditions in sub function - however current version is very clear.






share|improve this answer















Here is my solution - I assume that first hour in time-interval like ['08:00','12:00'] is always smaller than second hour. Instead of using your timestring_to_time and timestring_diff I write my own procedures to convert time to number - timeToNum and numToTime (you can easily extend them to include seconds: num=3600*hour + 60*min + sec, sec=num%60, h=floor(num/3600), min=floor((num-h*3600)/60) ):



<?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) {
preg_match('/(dd):(dd)/', $time, $matches);
return 60*$matches[1] + $matches[2];
}

function numToTime($num) {
$m = $num%60;
$h = intval($num/60) ;
return ($h>9? $h:"0".$h).":".($m>9? $m:"0".$m);

}

// substraction interval $b=[b0,b1] from interval $a=[a0,a1]
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;

var_dump(json_encode(["result"=>$valid_timeslots]));


Working example HERE.



Algorithm



Calculate $new_opening_hours from $old_opening_hours by substract one occupied slot from it. And repeat that operation for every slot (each time getting new oppening hours array)



To make substraction of two interwals I :




  1. convert time like "08:30" to number 08*60+30 = 510

  2. Solve problem of substract two interwals [a0,a1]-[b0,b1] which can have 5 cases (look on sub function) - as example [500,800]-[600,700] = [ [500,600], [700,800]],

  3. for each opening hours substract given occupied_slot and then clean and flat result to calc NEW openning hours


You can improve a little this solution by not conver time-number on each iteration but do it for each input data at the beginning and conver number-time for each output data at the end. And probably you can reduce number of conditions in sub function - however current version is very clear.







share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 18 '18 at 13:31

























answered Nov 17 '18 at 11:57









Kamil KiełczewskiKamil Kiełczewski

9,51685893




9,51685893













  • Works great. Thanks Kamil !

    – KeySee
    Nov 17 '18 at 13:20



















  • Works great. Thanks Kamil !

    – KeySee
    Nov 17 '18 at 13:20

















Works great. Thanks Kamil !

– KeySee
Nov 17 '18 at 13:20





Works great. Thanks Kamil !

– KeySee
Nov 17 '18 at 13:20


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53346699%2fdelete-timeslots-from-time-range-in-php%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Guess what letter conforming each word

Port of Spain

Run scheduled task as local user group (not BUILTIN)