Add range in PHP scheduling system slots - php

Helo! I am a beginner in programming and if anyone can help me change a code snippet I would appreciate it. I have a scheduling system with time slots, where the slots are generated from variables with the start date until the end date. I want to add a period between the start and end time, where nothing can be scheduled, for example, from 12:00 to 13:00, however, I don't know how to do it. Follow the code below:
$duration = 15;
$cleanup = 0;
$start = "08:00";
$end = "18:00";
function timeslots($duration, $cleanup, $start, $end){
$start = new DateTime($start);
$end = new DateTime($end);
$interval = new DateInterval("PT".$duration."M");
$cleanupInterval = new DateInterval("PT".$cleanup."M");
$slots = array();
for($intStart = $start; $intStart<$end; $intStart->add($interval)->add($cleanupInterval)){
$endPeriod = clone $intStart;
$endPeriod->add($interval);
if($endPeriod>$end){
break;
}
$slots[] = $intStart->format("H:iA")." - ". $endPeriod->format("H:iA");
}
return $slots;
}

Initialize your break-time start and end just like period. Check if your period start/end time is between the break-time. Then tell the period start-time to start from break-end.
SOLUTION
$duration = 15;
$cleanup = 0;
$start = "08:00";
$end = "18:00";
// add breakStart and breakEnd times
$breakStart = "12:00";
$breakEnd = "13:00";
function timeslots($duration, $cleanup, $start, $end){
$start = new DateTime($start);
$end = new DateTime($end);
$breakStart = new DateTime($breakStart);
$breakEnd = new DateTime($breakEnd);
$interval = new DateInterval("PT".$duration."M");
$cleanupInterval = new DateInterval("PT".$cleanup."M");
$slots = array();
for($intStart = $start; $intStart<$end; $intStart->add($interval)->add($cleanupInterval)){
$endPeriod = clone $intStart;
$endPeriod->add($interval);
if($endPeriod > $breakStart && $endPeriod < $breakEnd){
// this block when $endPeriod is between break-time
$intStart = $breakEnd; //so make the starting period time to start after break
$endPeriod->add($interval);
} elseif($intStart > $breakStart && $endPeriod < $breakEnd){
// this block when both $intStart and $endPeriod is between break-time
$intStart = $breakEnd; //so make the starting period time to start after break
$endPeriod->add($interval);
} elseif($intStart > $breakStart && $endPeriod > $breakEnd){
// this block when $intStart is between break-time
$intStart = $breakEnd; //so make the starting period time to start after break
$endPeriod->add($interval);
} else{
// your logic for creating the slots
if($endPeriod>$end){
break;
}
$slots[] = $intStart->format("H:iA")." - ". $endPeriod->format("H:iA");
}
}
return $slots;
}

You can refer to this https://www.codexworld.com/create-time-range-array-php/ where in you can have an array and you get the indexes for your start and end time and then you can avoid all indexes between the start_index and end_index.

Related

how to exclude an arbitrary period of time (1-2 hours) from the PHP array

There is an array of working hours, how to exclude lunch time from it?
$start = strtotime('8:00');
$end = strtotime('18:00');
$step = 1800; // 30 min
for($i = $start; $i <= $end; $i += $step)
{
$arTime[] = date('H:i', $i);
}
print_r($arTime);
A class that allows easy creation / manipulation / comparison of date and time, you can use DateTime to create the desired array.
Set a time-interval that is to be excluded from your results array using $lunchStart and $lunchStop.
$start = new DateTime('8:00');
$end = new DateTime('18:00');
$lunchStart = new DateTime('13:00');
$lunchStop = new DateTime('14:00');
while ($start <= $end) {
$slot = $start->format('H:i');
if ($start <= $lunchStart || $start >= $lunchStop) {
$arTime[] = $slot;
}
$start->add(new DateInterval('PT30M')); // 30 min interval
}
working demo

Find out by how many days two date ranges overlap in PHP

I have two arbitrary date ranges, for example:
2019-01-01 - 2019-01-10
and
2019-01-06 - 2019-01-20
How do I find out in PHP, by how many days these date ranges overlap? (In the example above it's 5 days)
Here's a solution using DateTime and DateInterval objects:
$range1 = '2019-01-01 - 2019-01-10';
$range2 = '2019-01-06 - 2019-01-20';
list($start, $end) = explode(' - ', $range1);
$start1 = new DateTime($start);
$end1 = new DateTime($end);
list($start, $end) = explode(' - ', $range2);
$start2 = new DateTime($start);
$end2 = new DateTime($end);
if ($end1 > $start1) {
$overlap = $end1->diff(min($start2, $end2));
}
else {
$overlap = $start1->diff(min($start2, $end2));
}
echo "overlap is " . ($overlap->format('%a') + 1) . " days";
Output
overlap is 5 days
Demo on 3v4l.org
Update
Here is a more robust version of the code that allows for arbitrary overlapping of the ranges (including one being contained entirely in the other):
function range_overlap($range1, $range2) {
list($start, $end) = explode(' - ', $range1);
$start = new DateTime($start);
$end = new DateTime($end);
$start1 = min($start, $end);
$end1 = max($start, $end);
list($start, $end) = explode(' - ', $range2);
$start = new DateTime($start);
$end = new DateTime($end);
$start2 = min($start, $end);
$end2 = max($start, $end);
// check for special cases
if ($start1 >= $start2 && $end1 <= $end2) {
// range1 completely contained inside range2
$overlap = $start1->diff($end1);
}
elseif ($start2 >= $start1 && $end2 <= $end1) {
// range2 completely contained inside range1
$overlap = $start2->diff($end2);
}
elseif ($end2 > $end1) {
// range1 ends first
$overlap = $start2->diff($end1);
}
else {
// range2 ends first
$overlap = $start1->diff($end2);
}
// if overlap is < 0 then there is no overlap
$overlap_days = $overlap->invert ? 0 : ($overlap->format('%a') + 1);
echo "overlap is $overlap_days days\n";
}
It can be called like this:
range_overlap('2019-01-01 - 2019-01-10', '2019-01-06 - 2019-01-20'); // 5 days
range_overlap('2019-01-01 - 2019-03-20', '2019-05-06 - 2019-04-20'); // no overlap
range_overlap('2019-01-10 - 2019-05-20', '2019-01-01 - 2019-05-20'); // 131 days
range_overlap('2019-01-06 - 2019-01-20', '2019-01-10 - 2019-01-01'); // 5 days
range_overlap('2019-01-30 - 2019-01-10', '2019-01-12 - 2019-01-15'); // 4 days
range_overlap('2019-02-01 - 2019-03-20', '2019-01-10 - 2019-02-28'); // 28 days
Demo on 3v4l.org
I'm not quite sure, what you want to do, but check this solution:
<?php
$date1 = strtotime('2019-01-01');
$date2 = strtotime('2019-01-10');
$date3 = strtotime('2019-01-06');
$date4 = strtotime('2019-01-20');
$dateDiff1 = $date2 - $date1;
$dateDiff2 = $date4 - $date3;
$finalDiff = $dateDiff2 - $dateDiff1;
echo round($finalDiff / (60 * 60 * 24));
Return:
5
More info about strtotime()
Notice: It will work only if second dates are always bigger that first ones. Your question is not clear.
Here's the function I use:
function nightsInRange(\Datetime $arriveDate, \Datetime $departDate, \Datetime $rangeStart, \Datetime $rangeEnd) : int
{
// just use the Y-m-d portion of date
$arriveDate = clone $arriveDate;
$arriveDate->setTime(0,0,0);
$departDate = clone $departDate;
$departDate->setTime(0,0,0);
$rangeStart = clone $rangeStart;
$rangeStart->setTime(0,0,0);
$rangeEnd = clone $rangeEnd;
$rangeEnd->setTime(0,0,0);
if ($arriveDate >= $departDate) {
throw new \InvalidArgumentException("arriveDate must be BEFORE departDate");
}
if ($rangeStart > $rangeEnd) {
throw new \InvalidArgumentException("rangeEnd must be greater than or equal to rangeStart");
}
$arriveDateInRange = ($arriveDate >= $rangeStart && $arriveDate <= $rangeEnd);
$departDateInRange = ($departDate >= $rangeStart && $departDate <= $rangeEnd);
if ($arriveDateInRange && $departDateInRange) {
// both dates inside range
$nightsInRange = $arriveDate->diff($departDate)->days;
} elseif ($arriveDateInRange && !$departDateInRange) {
// arrive inside, depart outside
$nightsInRange = $arriveDate->diff($rangeEnd)->days + 1;
} elseif (!$arriveDateInRange && $departDateInRange) {
// arrive outside, depart inside
$nightsInRange = $departDate->diff($rangeStart)->days;
} elseif ($arriveDate <= $rangeStart && $departDate >= $rangeEnd) {
// arrive before rangeStart, depart after rangeEnd
//
// Note that we add 1 to the date diff to get the range length. For
// example, the range from 2018-11-01 to 2018-11-30 is 30 days (not 29).
// A range with same start and end dates (e.g. 2018-11-01 to 2018-11-01)
// would have a range length of 1 day.
//
$nightsInRange = $rangeStart->diff($rangeEnd)->days + 1;
} else {
// no overlap
$nightsInRange = 0;
}
return $nightsInRange;
}

php - how to include time slot breaks using DateTime

I want to create time slots with start, end time & also break start and end.
I have achieved to generate the time slots but without the break between 11:10 - 11:25
LIVE CODE EXAMPLE
Input variables
$duration = 35; // how much the is the duration of a time slot
$cleanup = 0; // don't mind this
$start = '09:00'; // start time
$end = '12:00'; // end time
$break_start = '11:10'; // break start
$break_end = '11:25'; // break end
function availableSlots($duration, $cleanup, $start, $end) {
$start = new DateTime($start);
$end = new DateTime($end);
$interval = new DateInterval("PT" . $duration . "M");
$cleanupInterval = new DateInterval("PT" . $cleanup . "M");
$periods = array();
for ($intStart = $start; $intStart < $end; $intStart->add($interval)->add($cleanupInterval)) {
$endPeriod = clone $intStart;
$endPeriod->add($interval);
if ($endPeriod > $end) {
break;
}
$periods[] = $intStart->format('H:i A') . ' - ' . $endPeriod->format('H:i A');
}
return $periods;
}
Function will generate this output:
09:00 AM - 09:35 AM
09:35 AM - 10:10 AM
10:10 AM - 10:45 AM
10:45 AM - 11:20 AM
11:20 AM - 11:55 AM
What i need to achieve is 11:10am till 11:25am break period to be excluded from the results.
$break_start = '11:10';
$break_end = '11:25';
Try this...
I have modified your function
function availableSlots($duration, $cleanup, $start, $end, $break_start, $break_end) {
$start = new DateTime($start);
$end = new DateTime($end);
$break_start = new DateTime($break_start);
$break_end = new DateTime($break_end);
$interval = new DateInterval("PT" . $duration . "M");
$cleanupInterval = new DateInterval("PT" . $cleanup . "M");
$periods = array();
for ($intStart = $start; $intStart < $end; $intStart->add($interval)->add($cleanupInterval)) {
$endPeriod = clone $intStart;
$endPeriod->add($interval);
if(strtotime($break_start->format('H:i A')) < strtotime($endPeriod->format('H:i A')) && strtotime($endPeriod->format('H:i A')) < strtotime($break_end->format('H:i A'))){
$endPeriod = $break_start;
$periods[] = $intStart->format('H:i A') . ' - ' . $endPeriod->format('H:i A');
$intStart = $break_end;
$endPeriod = $break_end;
$intStart->sub($interval);
}else{
$periods[] = $intStart->format('H:i A') . ' - ' . $endPeriod->format('H:i A');
}
}
return $periods;
}
$duration = 35; // how much the is the duration of a time slot
$cleanup = 0; // don't mind this
$start = '09:00'; // start time
$end = '12:00'; // end time
$break_start = '11:10'; // break start
$break_end = '11:25'; // break end
availableSlots($duration, $cleanup, $start, $end, $break_start, $break_end);
The Result will be:
Array
(
[0] => 09:00 AM - 09:35 AM
[1] => 09:35 AM - 10:10 AM
[2] => 10:10 AM - 10:45 AM
[3] => 10:45 AM - 11:10 AM
[4] => 11:25 AM - 12:00 PM
)

I want to display time in a drop down using PHP. It will be be in chunks like 12:00, 12:15, 12:30 and so on

This is the code I am trying but it stops some time as now the value of i = 21 which is not <= 2. What should be the solution?
$pdts = array();
for($i = ltrim(date('H'), '0'); $i <= ltrim(date('H', time() + 14400), '0') * 2; $i++) {
for ($j = 15; $j <= 45; $j += 15) {
if ($j > ltrim(date('i'), '0') && ltrim(date('H'), '0') == $i) {
$date = date("H.i", strtotime("$i:$j"));
$value = $date."h";
$pdts[$value] = $date;
}
}
if (ltrim(date('i'), '0') != 0 && ltrim(date('H'), '0') != $i) {
$date = date("H.i", strtotime("$i:00"));
$value = $date."h";
$pdts[$value] = $date;
}
for ($k = 15; $k <= 45; $k += 15) {
if (ltrim(date('H'), '0') != $i) {
$date = date("H.i", strtotime("$i:$k"));
$value = $date . "h";
$pdts[$value] = $date;
}
}
}
As far as I understand you're trying to get 15-minutes chunks for the next 4 hours. There is built-in PHP DateTime / DateInterval / DatePeriod classes just for that. You can use them like that:
// current time - beginning of chunks
$begin = new DateTime();
// adjust $begin time for next '15/30/45/00' chunk
$next = $begin->format("i") % 15;
if ($next !== 0) {
$begin->modify('+' . (15 - $next) . 'minutes');
}
// time of last chunk
$end = clone $begin;
$end->modify("+4 hours");
// chunk interval (15 minutes)
$interval = new DateInterval('PT15M');
// date / time period onject
$timeRange = new DatePeriod($begin, $interval, $end);
$pdts = array();
foreach($timeRange as $time){
$pdts[] = $time->format("H:i");
}
Few words about code above:
1.Get current date & time. Current time is the beggining of time period to generate 15-minutes chuncks:
$begin = new DateTime();
2.Adjust current time to one of the 15-minutes chuncks. The easiest way to do it is to devide current amount of minutes by 15. If the reminder of devision is zero - than current time is OK and we can start from it. Otherwise we need to add (15 - reminder) minutes to current time to get valid start time:
$next = $begin->format("i") % 15;
if ($next !== 0) {
$begin->modify('+' . (15 - $next) . 'minutes');
}
3.To get end time of time period we need to add 4 hours to start time:
$end = clone $begin;
$end->modify("+4 hours");
4.We need to create time interval object with chunk duration:
$interval = new DateInterval('PT15M');
5.Create date period object (it will do all job for us)
$timeRange = new DatePeriod($begin, $interval, $end);
6.Iterate through date period object to gett all chunks
$pdts = array();
foreach($timeRange as $time){
$pdts[] = $time->format("H:i");
}

Php DateTime and timestamp calculation

I'm trying to get an array of dates to populate a calendar and I'm facing an the issue of infinite loop.
Here is the code:
$month_length = 1;
$day_begin = 9;
$day_end = 19;
$event_interval = 15;
$date = new DateTime();
$today_date_name = $date->format("D");
$today_date_number = $date->format("j");
$today_date_month = $date->format("M");
$today_date_month_number =$date->format("n");
$today_date_year = $date->format("Y");
for ($length = 0; $length <= ($month_length - 1); $length++)
{
$date->modify("+$length month");
$current_date_name = $date->format("D");
$current_date_number = $date->format("j");
$current_date_month = $date->format("M");
$current_date_month_number = $date->format("n");
$current_date_year = $date->format("Y");
//calculate the length of the month
$current_month_length = cal_days_in_month(CAL_GREGORIAN, $current_date_month_number, $current_date_year);
if($current_date_month_number != $today_date_month_number){
// if we are not in the current month, start the second loop
// the first of the month.
$current_date_number = 1;
}
for($current_date_number; $current_date_number <= $current_month_length; $current_date_number++)
{
$date->setDate($current_date_year, $current_date_month_number, $current_date_number);
//set the ending before because of the loop;
//set the ending of the day
$date->setTime($day_end, 0, 0);
//get the timestamp of beginning
$ending_timestamp = $date->format("U");
//set the beginning of the day
$date->setTime($day_begin, 0, 0);
//get the timestamp of beginning
$beginning_timestamp = $date->format("U");
$day_length = $ending_timestamp - $beginning_timestamp;
//60 seconds for 1min
$interval = 60 * $event_interval;
for($the_timestamp = 0; $the_timestamp <= $day_length ; $the_timestamp + $interval)
{
$current_timestamp = $beginning_timestamp + $the_timestamp;
$date->setTimestamp($current_timestamp);
$final_array[$current_date_year][$current_date_number . " " . $current_date_month][$current_timestamp] = $date->format("H : i");
}
}
}
the last "for" loop ends with an infinite loop and so reaches the maximum execution time, it must be done under 30s.
Your code is far too complex, you are doing something wrong. The following will generate an array representing a whole year:-
$start = new \DateTime('1st January');
$end = new \DateTime('31st December');
$interval = new \DateInterval('P1D');
$period = new \DatePeriod($start, $interval, $end->modify('+ 1 day'));
$year = array();
foreach($period as $day){
$year[$day->format('M')][(int)$day->format('d')] = $day->format('D');
}
var_dump($year);
See it working
for($the_timestamp = 0; $the_timestamp <= $day_length ; $the_timestamp + $interval){
//Stuff
}
This loop doesn't increment $the_timestamp which is why it loops forever.
The last part should be $the_timestamp += $interval which is equivalent to $the_timestamp = $the_timestamp + $interval.

Categories