Next Business Day of Given Date in PHP

Next business day of given date in PHP

Next Weekday

This finds the next weekday from a specific date (not including Saturday or Sunday):

echo date('Y-m-d', strtotime('2011-04-05 +1 Weekday'));

You could also do it with a date variable of course:

$myDate = '2011-04-05';
echo date('Y-m-d', strtotime($myDate . ' +1 Weekday'));

UPDATE: Or, if you have access to PHP's DateTime class (very likely):

$date = new DateTime('2018-01-27');
$date->modify('+7 weekday');
echo $date->format('Y-m-d');

Want to Skip Holidays?:

Although the original poster mentioned "I don't need to consider holidays", if you DO happen to want to ignore holidays, just remember - "Holidays" is just an array of whatever dates you don't want to include and differs by country, region, company, person...etc.

Simply put the above code into a function that excludes/loops past the dates you don't want included. Something like this:

$tmpDate = '2015-06-22';
$holidays = ['2015-07-04', '2015-10-31', '2015-12-25'];
$i = 1;
$nextBusinessDay = date('Y-m-d', strtotime($tmpDate . ' +' . $i . ' Weekday'));

while (in_array($nextBusinessDay, $holidays)) {
$i++;
$nextBusinessDay = date('Y-m-d', strtotime($tmpDate . ' +' . $i . ' Weekday'));
}

I'm sure the above code can be simplified or shortened if you want. I tried to write it in an easy-to-understand way.

Calculate business days

Here's a function from the user comments on the date() function page in the PHP manual. It's an improvement of an earlier function in the comments that adds support for leap years.

Enter the starting and ending dates, along with an array of any holidays that might be in between, and it returns the working days as an integer:

<?php
//The function returns the no. of business days between two dates and it skips the holidays
function getWorkingDays($startDate,$endDate,$holidays){
// do strtotime calculations just once
$endDate = strtotime($endDate);
$startDate = strtotime($startDate);

//The total number of days between the two dates. We compute the no. of seconds and divide it to 60*60*24
//We add one to inlude both dates in the interval.
$days = ($endDate - $startDate) / 86400 + 1;

$no_full_weeks = floor($days / 7);
$no_remaining_days = fmod($days, 7);

//It will return 1 if it's Monday,.. ,7 for Sunday
$the_first_day_of_week = date("N", $startDate);
$the_last_day_of_week = date("N", $endDate);

//---->The two can be equal in leap years when february has 29 days, the equal sign is added here
//In the first case the whole interval is within a week, in the second case the interval falls in two weeks.
if ($the_first_day_of_week <= $the_last_day_of_week) {
if ($the_first_day_of_week <= 6 && 6 <= $the_last_day_of_week) $no_remaining_days--;
if ($the_first_day_of_week <= 7 && 7 <= $the_last_day_of_week) $no_remaining_days--;
}
else {
// (edit by Tokes to fix an edge case where the start day was a Sunday
// and the end day was NOT a Saturday)

// the day of the week for start is later than the day of the week for end
if ($the_first_day_of_week == 7) {
// if the start date is a Sunday, then we definitely subtract 1 day
$no_remaining_days--;

if ($the_last_day_of_week == 6) {
// if the end date is a Saturday, then we subtract another day
$no_remaining_days--;
}
}
else {
// the start date was a Saturday (or earlier), and the end date was (Mon..Fri)
// so we skip an entire weekend and subtract 2 days
$no_remaining_days -= 2;
}
}

//The no. of business days is: (number of weeks between the two dates) * (5 working days) + the remainder
//---->february in none leap years gave a remainder of 0 but still calculated weekends between first and last day, this is one way to fix it
$workingDays = $no_full_weeks * 5;
if ($no_remaining_days > 0 )
{
$workingDays += $no_remaining_days;
}

//We subtract the holidays
foreach($holidays as $holiday){
$time_stamp=strtotime($holiday);
//If the holiday doesn't fall in weekend
if ($startDate <= $time_stamp && $time_stamp <= $endDate && date("N",$time_stamp) != 6 && date("N",$time_stamp) != 7)
$workingDays--;
}

return $workingDays;
}

//Example:

$holidays=array("2008-12-25","2008-12-26","2009-01-01");

echo getWorkingDays("2008-12-22","2009-01-02",$holidays)
// => will return 7
?>

Calculating next business day with a time-of-day cutoff

So you're looking for a function to return the next business day that is a certain number of days from the current day – unless it's on a weekend or after 2PM in which case you want the next business day that's a certain number of days from the following business day? This should do that. I kept it all pretty sprawled out so it should be clear to understand.

function getDeliveryDay(int $days, int $date = null):int {
if ($date === null) {
$date = time();
}
$base = "now";
if (date("H", $date) >= 14) {
// if it's late, calculate from tomorrow
$days++;
}
if (in_array(date("w", $date), [0, 6])) {
// if it's a weekend, calculate from monday
$base = "next monday";
}
for ($i = 0; $i <= $days; $i++) {
// make sure delivery day isn't Saturday or Sunday
// don't count those days in processing time either
$result = strtotime("$base +$i days", $date);
$w = date("w", $result);
if (in_array($w, [0, 6])) $days++;
}
return $result;
}

Testing (at 3:00 on Thursday):

echo date("l, F jS", getDeliveryDay(0)) . "\n"; // should be Friday
echo date("l, F jS", getDeliveryDay(1)) . "\n"; // should be Monday
echo date("l, F jS", getDeliveryDay(2)) . "\n"; // should be Tuesday
echo date("l, F jS", getDeliveryDay(3)) . "\n"; // should be Wednesday
echo date("l, F jS", getDeliveryDay(4)) . "\n"; // should be Thursday
echo date("l, F jS", getDeliveryDay(5)) . "\n"; // should be Friday

Output:

Friday, April 5th
Monday, April 8th
Tuesday, April 9th
Wednesday, April 10th
Thursday, April 11th
Friday, April 12th

In your original code:

$dateStandardMinMG = date("l, F jS", getDeliveryDay(2));
$dateStandardMaxMG = date("l, F jS", getDeliveryDay(4));
$dateExpressMinMG = date("l, F jS", getDeliveryDay(0));
$dateExpressMaxMG = date("l, F jS", getDeliveryDay(1));

Add two business days to a date array excluding holiday dates

You could do something as simple as use a while loop.

$date = '2017-07-25';
$reserved = ['2017-07-27', '2017-07-28'];
$days = 2;

while ($days > 0) {
$date = date('Y-m-d', strtotime($date . ' +1 weekday'));
if (! in_array($date, $reserved)) $days--;
}

var_dump($date);

PHP - get next weekday but skip Monday between 13:00 Fri and 24:00 Sun

You need to find a "start" date to increment from, so rather than working out a delivery date, work out the current date, see where it fits into your rules, then increment the "start" date until it passes your rules.

Then you can figure out your delivery date:

function get_next_workday() {
$bankhols = array('25-Dec-2018', '26-Dec-2018');
$increment_days = 5;

$dayincrease = 1; // Used if today happens to fall on a weekend / fri > 1pm
$start_date = date('d-M-Y', strtotime('now')); // Assume the "start" date is now
$nownum = (int)date('w', strtotime('now'));

// Continue to increment the "start" date if it's a Fri > 1pm, Sat, or Sun
while ( in_array($nownum, array(6,0)) OR ($nownum===5 AND (int)date('H')>13) ) {
$newdate = strtotime('+'.$dayincrease.' day');
$nownum = date('w', $newdate);
$start_date = date('d-M-Y', $newdate);
$dayincrease++;
}

// Now we have a "start" date to work from
// (either now, or, the following monday),
// we can now find the delivery dates

for ($i=1; $i<=$increment_days; $i++) {
// Increment the Delivery Date another day (from our "start" date)
$delivery = strtotime($start_date.'+'.$i.' day');
// As long as it's a Mon, Tue, Wed, Thur, Fri, and Not Bank Holidays, it's a delivery date!
if (in_array((int)date('w', $delivery), array(1,2,3,4,5)) AND !in_array(date('d-M-Y', $delivery), $bankhols)) return $delivery;
}
}

echo date('l, jS F', get_next_workday());

The first "while" loop implements your after Friday 1pm before Sunday midnight check, if the current time IS within that window, it keeps incrementing a day until if falls out the window.

Then the delivery date can be found using that start date.

Hope this helps.

Javascript or PHP create new date but only count business hours

Hopefully the following is something like what you need (free code is free code…). There are lots of quirks with dates, so the following tries a very simple algorithm to add hours in chunks, skip over weekends, and deal with any overruns. There are a few business rules:

  1. If the start is after the finish of one business day and before the start time of the next, the start is moved to the start of the next business day. So if the start is 18:00 Sunday, it's moved to 08:00 Monday.
  2. If the date provided isn't even hours, the minutes and seconds are preserved
  3. The returned date may have a time of 18:00, e.g. if the start is 08:00 on a work day and 10 hours is added the returned date will be 18:00 on the same day.
  4. If the start is 09:00 and 10 hours are added, the returned date will be 09:00 the next day

It uses a loop to add the days and hours. It could use logic like Paul S to work out how many days and go that way (I'd rather use setDate than setHours, but I guess that's just me) but this is simpler for my brain. Now that the algorithm is correct, it's easy to simplify the code (and maybe do the non-looping hours thing).

Hopefully the comments in the code are sufficient. Start hour, end hour and weekend days are configurable.

/*  Add work hours to date, but only from 08:00 up to 18:00**  and skip weekends (Saturday, Sunday)**  Work in whole hours, bus****  @param   {Date} date  - Date to add hours to**  @param {number} hours - Integer number of hours to add**  @returns {Date}       - new Date with work hours added.*/function addWorkHours(date, hours) {  // Setable parameters  var startHour = 8;  var endHour = 18;  // Weekend days to skip, one day for Sunday (0), two for Saturday (6)  var weekendDays = {'6':2, '0':1};  // Western: Saturday, Sunday //  var weekendDays = {'4':2, '5':1};  // Arabic: Thursday, Friday    // Calculated values  var date = new Date(+date);  // Copy of provided date  var hrsPerDay = endHour - startHour;  var hrsToAdd, hrsLeftToday;  // Make sure hours is a number  hours = +hours;
// If before start time, set to start if (date.getHours() < startHour) { date.setHours(startHour,0,0,0); // If after end time, set to start on next day } else if (date.getHours() > endHour) { date.setDate(date.getDate() + 1); date.setHours(startHour,0,0,0); } // If on a weekend, skip to start hour on next working day if (date.getDay() in weekendDays) { date.setDate(date.getDate() + weekendDays[date.getDay()]); date.setHours(startHour, 0,0,0); } // Add hours to date during business hours until exhaused while (hours) { // Get number of work hours left today hrsLeftToday = endHour - date.getHours(); // If hrsLeftToday is 0 and there are hours to add, go to start tomorrow if (!hrsLeftToday && hours) { date.setDate(date.getDate() + 1); date.setHours(startHour); } // Get hours to end time or remaining hours, whichever is less hrsToAdd = hrsLeftToday > hours? hours : hrsLeftToday; // Adjust hours hours -= hrsToAdd; // Add the hours, but don't go past end time // If hours to add is equal the hours in a day, and there are // more hours to add, just add one day if (hrsToAdd == hrsPerDay && hours) { date.setDate(date.getDate() + 1); } else { // Otherwise, set the hours date.setHours(date.getHours() + hrsToAdd); } // If hours are zero, tidy any overlap past end hour if there is any if (!hours) { // If gone past end time in hours, run into next day if (date.getHours() > endHour) { // Get overrun, add a day and add back overrun date.setDate(date.getDate() + 1); date.setHours(startHour + date.getHours() - endHour); }
// Do the same with minutes if (date.getHours() == endHour && (date.getMinutes() || date.getSeconds())) { date.setDate(date.getDate() + 1); date.setHours(startHour); } }
// If now on a weekend, skip to next working day if (date.getDay() in weekendDays) { date.setDate(date.getDate() + weekendDays[date.getDay()]); } } return date;}// Helper to parse ISO input date string as localfunction parseDate(s) { var b = s.split(/\D/); return new Date(b[0],b[1]-1,b[2],b[3]||0,b[4]||0);}
var d = parseDate(document.getElementById('startdate').value);var h = document.getElementById('hrs').value;document.getElementById('result').innerHTML = 'Start: ' + d + '<br>End: ' + addWorkHours(d, h);
Date (yyyy-mm-dd [hh:mm:ss])<input type="text" id="startdate" value="2015-12-07"><br>Hours to add (integer)<input type="text" id="hrs" value="100"><br><button onclick=" var d = parseDate(document.getElementById('startdate').value); var h = document.getElementById('hrs').value; document.getElementById('result').innerHTML = 'Start: ' + d + '<br>End: ' + addWorkHours(d, h); ">Add hours</button><div id="result"></div>

How to get the first business day in a new month after the weekend?

This function will do the trick. Here is the logic:

  • Create a datetime array for all days in the given month.
  • Get the day numbers.
  • Create a logical array, true from the first Monday onwards (so after the first weekend, accounting for the last day of the previous month being a Sunday).
  • Create another logical array using isbusday to exclude Mondays which aren't working days.
  • Finding the first day number where these two logical arrays are true, therefore the first business day after the weekend.

Code:

function d = fbusdateAferWE( y, m )
% Inputs: y = year, m = month
% Outputs: day of the month, first business day after weekend

% Create array of days for the given month
dates = datetime( y, m, 1 ):days(1):datetime( y, m, eomday( y, m ) );
% Get the weekday numbers to find first Monday, 1 = Sunday
dayNum = weekday( dates );
% Create the logical array to determine days from first Monday
afterFirstWeekend = ( cumsum(dayNum==2) > 0 ).';
% Get first day which is afterFirstWeekend and a business day.
d = find( afterFirstWeekend & isbusday( dates ), 1 );
end

You could probably speed this up (although it will be pretty rapid already) by not looking at the whole month, but say just 2 weeks. I used eomday to get the last day of the month, which means I don't have to make assumptions about a low number of holiday days in the first week or anything.


Edit: Working with datenum speeds it up by half (C/O JohnAndrews):

function d = fbusdateAferWE( y, m )
% Inputs: y = year, m = month
% Outputs: day of the month, first business day after weekend

% Create array of days for (first 2 weeks of) the given month
dates = datenum(datetime(y,m,1)):datenum(datetime(y,m,eomday(y,m)))-14;
% Get the weekday numbers to find first Monday, 1 = Sunday
dayNum = weekday( dates );
% Create the logical array to determine days from first Monday
afterFirstWeekend = ( cumsum(dayNum==2) > 0 ).';
% Get first day which is afterFirstWeekend and a business day.
d = find( afterFirstWeekend & isbusday( dates ), 1 );
end

Working days (Mon-Fri) in PHP

If you are limiting to weekdays use the string weekdays.

echo date ( 'Y-m-j' , strtotime ( '3 weekdays' ) );

This should jump you ahead by 3 weekdays, so if it is Thursday it will add the additional weekend time.

Source: http://www.php.net/manual/en/datetime.formats.relative.php

Calculating next business day

I think this has been asked before, Next business day of given date in PHP, but here it is using Zend_Date:

$now = new Zend_Date();
if (($now->get(Zend_Date::WEEKDAY_DIGIT) % 6 == 0)
|| ($now->isLater('17:00:00', Zend_Date::TIMES))
) {
$now->set(
strtotime('+1 weekday', $now->toString(Zend_Date::TIMESTAMP)),
Zend_Date::TIMESTAMP
);
}
echo $now->toString(Zend_Date::W3C);


Related Topics



Leave a reply



Submit