php - how to include time slot breaks using DateTime - php

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
)

Related

Add range in PHP scheduling system slots

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.

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;
}

Periods of 20 days between two dates including the last one

I need to loop through the days between two dates in intervals of 20 days, including the last date. For example, between the dates 2019/01/01 and 2019/01/27 should return the following ranges:
2019-01-01 => 2019-01-20
2019-01-21 => 2019-02-09
2019-02-10 => 2019-02-27
I have tried with this code:
$start = new DateTime('2019-01-01');
$end = new DateTime('2019-02-27');
$interval = new DateInterval('P20D');
$period = new DatePeriod($start, $interval, $end, DatePeriod::EXCLUDE_START_DATE);
$from = $start->format('Y-m-d');
foreach ($period as $day) {
$to = $day->format('Y-m-d');
echo $from . ' => ' . $to . '<br>';
$from = $day->modify('+1 day')->format('Y-m-d');
}
Output:
2019-01-01 => 2019-01-21
2019-01-22 => 2019-02-10
This code has two problems, does not include the last period (no longer containing 20 days) and is advanced one day. How can I solve these problems?
This is probably more simply done by just incrementing the $start value by the $interval until it is greater than the $end value. Note that the interval needs to be 19 days to make a 20-day (inclusive of start and end) period.
$start = new DateTime('2019-01-01');
$end = new DateTime('2019-02-27');
$interval = new DateInterval('P19D');
while ($start < $end) {
echo $start->format('Y-m-d') . ' => ';
$start->add($interval);
echo min($start, $end)->format('Y-m-d') . "\n";
$start->add(new DateInterval('P1D'));
}
Output:
2019-01-01 => 2019-01-20
2019-01-21 => 2019-02-09
2019-02-10 => 2019-02-27
Demo on 3v4l.org
Update
Here is a version of the code that also skips weekends:
$start = new DateTime('2019-01-01');
$end = new DateTime('2019-02-27');
$interval = new DateInterval('P1D');
$days = 19;
while ($start < $end) {
echo $start->format('Y-m-d') . ' => ';
for ($i = 0; $i < $days; ) {
$start->add($interval);
$day_of_week = $start->format('N');
if ($day_of_week == 6 || $day_of_week == 7) continue;
$i++;
}
echo min($start, $end)->format('Y-m-d') . "\n";
$start->add($interval);
}
Demo on 3v4l.org

PHP how do I get previous Quarter start and end date

I know how to get the previous Quarter number how to turn that into date ranges especially when it goes into the previous year?
$Quarter = floor((date('n') - 1) / 3);
Here you go:
function getQuarter(\DateTime $DateTime) {
$y = $DateTime->format('Y');
$m = $DateTime->format('m');
switch($m) {
case $m >= 1 && $m <= 3:
$start = '01/01/'.$y;
$end = (new DateTime('03/1/'.$y))->modify('Last day of this month')->format('m/d/Y');
$title = 'Q1 '.$y;
break;
case $m >= 4 && $m <= 6:
$start = '04/01/'.$y;
$end = (new DateTime('06/1/'.$y))->modify('Last day of this month')->format('m/d/Y');
$title = 'Q2 '.$y;
break;
case $m >= 7 && $m <= 9:
$start = '07/01/'.$y;
$end = (new DateTime('09/1/'.$y))->modify('Last day of this month')->format('m/d/Y');
$title = 'Q3 '.$y;
break;
case $m >= 10 && $m <= 12:
$start = '10/01/'.$y;
$end = (new DateTime('12/1/'.$y))->modify('Last day of this month')->format('m/d/Y');
$title = 'Q4 '.$y;
break;
}
return array(
'start' => $start,
'end' => $end,
'title'=>$title,
'start_nix' => strtotime($start),
'end_nix' => strtotime($end)
);
}
print_r(getQuarter(new DateTime()));
Output
Array
(
[start] => 10/01/2018
[end] => 12/31/2018
[title] => Q4 2018
[start_nix] => 1538377200
[end_nix] => 1546243200
)
Sandbox
Your in luck I wrote this a wile ago ... This is sort of the brute force way of doing it, but hey it works. There is probably a "fancier" way, but whatever...
UPDATE
Based on some comments using DateTime has many advantages, besides just making the code in the function more concise. For example to get a previous quarter:
print_r(getQuarter((new DateTime())->modify('-3 Months'));
Output
Array
(
[start] => 07/01/2018
[end] => 09/30/2018
[title] => Q3 2018
[start_nix] => 1530428400
[end_nix] => 1538290800
)
Sandbox
Here the extra parentheses are important (around new DateTime)
(new DateTime())->modify('-3 Months');
This causes the constructor to return the instance of the object, which lets you immediately call modify on it. It's equivalent to doing this:
$DateTime = new DateTime();
$DateTime->modify('-3 Months');
But without creating a local variable.
And on the same token you can get the next quarter by doing
print_r(getQuarter((new DateTime())->modify('+3 Months'));
Another example of this is in the function itself (specifically):
(new DateTime('03/1/'.$y))->modify('Last day of this month')
What this does is get the last day of whatever month the DateTime object has, in this case it's 3. So we don't have to even think of how many days that month has, it just returns the correct number. These are Relative Date formats
http://php.net/manual/en/datetime.formats.relative.php
One last one that may be of use to you is this one first day of ? this year where the ? is the month name. For example:
print_r(getQuarter((new DateTime())->modify('first day of january this year')));
print_r(getQuarter((new DateTime())->modify('first day of april this year')));
print_r(getQuarter((new DateTime())->modify('first day of july this year')));
print_r(getQuarter((new DateTime())->modify('first day of october this year')));
Effectively this will give you each quarter this year.
Hope that helps.
A simple one-liner for each start and end date would be:
$start = (new DateTime('first day of -' . (((date('n') - 1) % 3) + 3) . ' month'))->format('Y-m-d'); # first day of previous quarter
$end = (new DateTime('last day of -' . (((date('n') - 1) % 3) + 1) . ' month'))->format('Y-m-d'); # last day of previous quarter
DateTime can work with relative values so you can describe which date you want.
I am using this code. It uses DateTime object and its methods.
The main method is getCurrentQuarterStartDate(), the other 2 methods use it.
You can obtain not only dates of previous quarter, but of any other quarter in the past and in the future.
<?php
echo "Current quarter start: \n";
$dt = getCurrentQuarterStartDate();
var_dump($dt->format('Y-m-d'));
echo "Current quarter end: \n";
$dt = getRelativeQuarterEndDate(0);
var_dump($dt->format('Y-m-d'));
echo "Next quarter start: \n";
$dt = getRelativeQuarterStartDate(1);
var_dump($dt->format('Y-m-d'));
echo "Next quarter end: \n";
$dt = getRelativeQuarterEndDate(1);
var_dump($dt->format('Y-m-d'));
echo "Prev quarter start: \n";
$dt = getRelativeQuarterStartDate(-1);
var_dump($dt->format('Y-m-d'));
echo "Prev quarter end: \n";
$dt = getRelativeQuarterEndDate(-1);
var_dump($dt->format('Y-m-d'));
function getCurrentQuarterStartDate()
{
$dt = new DateTime('now', new DateTimeZone('Europe/Prague'));
$currentMonth = (int)$dt->format('m');
$currentYear = (int)$dt->format('Y');
$currentQuartalNr = ceil($currentMonth / 3); // returns 0.333-4 and it is ceiled to 1-4
$currentQuartalStartMonth = $currentQuartalNr * 3 - 2; // returns 1,4,7 or 10
$dt->setDate($currentYear, $currentQuartalStartMonth, 1);
return $dt;
}
/**
* #param $offset 0 = current quarter, 1 = next, -1 = prev, -2, +5 ...
*/
function getRelativeQuarterStartDate($offset = 0)
{
$currentQStartDate = getCurrentQuarterStartDate();
if ($offset == 0) {
return $currentQStartDate;
}
if ($offset > 0) {
$currentQStartDate->add(new \DateInterval('P' . $offset*3 . 'M'));
return $currentQStartDate;
}
if ($offset < 0) {
$currentQStartDate->sub(new \DateInterval('P' . abs($offset)*3 . 'M'));
return $currentQStartDate;
}
}
/**
* #param $offset 0 = current quarter, 1 = next, -1 = prev, -2, +5 ...
*/
function getRelativeQuarterEndDate($offset = 0)
{
$dt = getCurrentQuarterStartDate();
if ($offset >= 0) {
$dt->add(new \DateInterval('P' . $offset*3 . 'M'));
}
if ($offset < 0) {
$dt->sub(new \DateInterval('P' . abs($offset)*3 . 'M'));
}
$dt->add(new \DateInterval('P3M'));
$dt->sub(new \DateInterval('P1D'));
return $dt;
}
Find Previous Quarter start and end date
function getpreviousQuarterData(\DateTime $DateTime) {
$y = $DateTime->format('Y');
$m = $DateTime->format('m');
switch($m) {
case $m >= 1 && $m <= 3:
$start = '10/01/'.$y-1;
$end = (new DateTime('12/1/'.$y-1))->modify('Last day of this month')->format('m/d/'.$y-1);
$title = 'Q4 '.$y-1;
break;
case $m >= 4 && $m <= 6:
$start = '01/01/'.$y;
$end = (new DateTime('03/1/'.$y))->modify('Last day of this month')->format('m/d/Y');
$title = 'Q1 '.$y;
break;
case $m >= 7 && $m <= 9:
$start = '04/01/'.$y;
$end = (new DateTime('06/1/'.$y))->modify('Last day of this month')->format('m/d/Y');
$title = 'Q2 '.$y;
break;
case $m >= 10 && $m <= 12:
$start = '07/01/'.$y;
$end = (new DateTime('09/1/'.$y))->modify('Last day of this month')->format('m/d/Y');
$title = 'Q3 '.$y;
break;
}
return array(
'start' => $start,
'end' => $end,
'title'=>$title,
'start_nix' => strtotime($start),
'end_nix' => strtotime($end)
);
}

confusing date manipulations

tying to go through all sundays like this:
/*self::WEEK = 604800*/
/* 3600 * 24 * 7 looks like 7 days for me*/
$start = 1286053200;
for ($i=0; $i < 10; $i++) {
echo date('d.m.Y H:i:s.u, D (z)', $start) . ' (' . $start . ')<br/>';
$start += self::WEEK;
}
but in some case i see this:
03.10.2010 00:00:00.000000, Sun (275) (1286053200)
10.10.2010 00:00:00.000000, Sun (282) (1286658000)
17.10.2010 00:00:00.000000, Sun (289) (1287262800)
24.10.2010 00:00:00.000000, Sun (296) (1287867600)
31.10.2010 00:00:00.000000, Sun (303) (1288472400)
06.11.2010 23:00:00.000000, Sat (309) (1289077200) <--
13.11.2010 23:00:00.000000, Sat (316) (1289682000)
20.11.2010 23:00:00.000000, Sat (323) (1290286800)
27.11.2010 23:00:00.000000, Sat (330) (1290891600)
04.12.2010 23:00:00.000000, Sat (337) (1291496400)
in other words lost an hour!? where?!
Try strtotime()
$start = 1286053200;
for ($i=0; $i < 10; $i++) {
echo date('d.m.Y H:i:s.u, D (z)', strtotime(' +' . ($i * 7) . ' DAYS',$start)) . ' (' . $start . ')<br/>';
}
Daylight saving?
Try gmdate() instead.
I created a function for a similar problem. Maybe you'll find it useful.
function getDays($year, $dayofweek, $format, $timezone='UTC')
{
$fridays = array();
$startDate = new DateTime("{$year}-01-01 {$dayofweek}", new DateTimezone($timezone));
$year++;
$endDate = new DateTime("{$year}-01-01", new DateTimezone($timezone));
$int = new DateInterval('P7D');
foreach(new DatePeriod($startDate, $int, $endDate) as $d) {
$fridays[] = $d->format($format);
}
return $fridays;
}
$sundays = getDays('2010', 'Sunday', 'd.m.Y H:i:s.u, D (z)', 'America/New_York');
var_dump($sundays);

Categories