I need a method for adding \"business days\" in PHP. For example, Friday 12/5 + 3 business days = Wednesday 12/10.
At a minimum I need the code to understand weekend
I just get my function working based on Bobbin and mcgrailm code, adding some things that worked perfect to me.
function add_business_days($startdate,$buisnessdays,$holidays,$dateformat){
$enddate = strtotime($startdate);
$day = date('N',$enddate);
while($buisnessdays > 0){ // compatible with 1 businessday if I'll need it
$enddate = strtotime(date('Y-m-d',$enddate).' +1 day');
$day = date('N',$enddate);
if($day < 6 && !in_array(date('Y-m-d',$enddate),$holidays))$buisnessdays--;
}
return date($dateformat,$enddate);
}
// as a parameter in in_array function we should use endate formated to
// compare correctly with the holidays array.
Brute attempt to detect working time - Monday to Friday 8am-4pm:
if (date('N')<6 && date('G')>8 && date('G')<16) {
// we have a working time (or check for holidays)
}
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
?>
You can try this function which is more simple.
function getWorkingDays($startDate, $endDate)
{
$begin = strtotime($startDate);
$end = strtotime($endDate);
if ($begin > $end) {
return 0;
} else {
$no_days = 0;
while ($begin <= $end) {
$what_day = date("N", $begin);
if (!in_array($what_day, [6,7]) ) // 6 and 7 are weekend
$no_days++;
$begin += 86400; // +1 day
};
return $no_days;
}
}
calculate workdays between two dates including holidays and custom workweek
The answer is not that trivial - thus my suggestion would be to use a class where you can configure more than relying on simplistic function (or assuming a fixed locale and culture). To get the date after a certain number of workdays you'll:
Functional Approach
/**
* @param days, int
* @param $format, string: dateformat (if format defined OTHERWISE int: timestamp)
* @param start, int: timestamp (mktime) default: time() //now
* @param $wk, bit[]: flags for each workday (0=SUN, 6=SAT) 1=workday, 0=day off
* @param $holiday, string[]: list of dates, YYYY-MM-DD, MM-DD
*/
function working_days($days, $format='', $start=null, $week=[0,1,1,1,1,1,0], $holiday=[])
{
if(is_null($start)) $start = time();
if($days <= 0) return $start;
if(count($week) != 7) trigger_error('workweek must contain bit-flags for 7 days');
if(array_sum($week) == 0) trigger_error('workweek must contain at least one workday');
$wd = date('w', $start);//0=sun, 6=sat
$time = $start;
while($days)
{
if(
$week[$wd]
&& !in_array(date('Y-m-d', $time), $holiday)
&& !in_array(date('m-d', $time), $holiday)
) --$days; //decrement on workdays
$wd = date('w', $time += 86400); //add one day in seconds
}
$time -= 86400;//include today
return $format ? date($format, $time): $time;
}
//simple usage
$ten_days = working_days(10, 'D F d Y');
echo '<br>ten workingdays (MON-FRI) disregarding holidays: ',$ten_days;
//work on saturdays and add new years day as holiday
$ten_days = working_days(10, 'D F d Y', null, [0,1,1,1,1,1,1], ['01-01']);
echo '<br>ten workingdays (MON-SAT) disregarding holidays: ',$ten_days;
Holiday calculation is non-standard in each State. I am writing a bank application which I need some hard business rules for but can still only get a rough standard.
/**
* National American Holidays
* @param string $year
* @return array
*/
public static function getNationalAmericanHolidays($year) {
// January 1 - New Year’s Day (Observed)
// Calc Last Monday in May - Memorial Day strtotime("last Monday of May 2011");
// July 4 Independence Day
// First monday in september - Labor Day strtotime("first Monday of September 2011")
// November 11 - Veterans’ Day (Observed)
// Fourth Thursday in November Thanksgiving strtotime("fourth Thursday of November 2011");
// December 25 - Christmas Day
$bankHolidays = array(
$year . "-01-01" // New Years
, "". date("Y-m-d",strtotime("last Monday of May " . $year) ) // Memorial Day
, $year . "-07-04" // Independence Day (corrected)
, "". date("Y-m-d",strtotime("first Monday of September " . $year) ) // Labor Day
, $year . "-11-11" // Veterans Day
, "". date("Y-m-d",strtotime("fourth Thursday of November " . $year) ) // Thanksgiving
, $year . "-12-25" // XMAS
);
return $bankHolidays;
}