Calculate business days

PhpCalendarDate

Php Problem Overview


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 weekends, but ideally it should account for US federal holidays as well. I'm sure I could come up with a solution by brute force if necessary, but I'm hoping there's a more elegant approach out there. Anyone?

Thanks.

Php Solutions


Solution 1 - Php

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
?>

Solution 2 - Php

Get the number of working days without holidays between two dates :

###Use example:

echo number_of_working_days('2013-12-23', '2013-12-29');

###Output:

3

###Function:

function number_of_working_days($from, $to) {
    $workingDays = [1, 2, 3, 4, 5]; # date format = N (1 = Monday, ...)
    $holidayDays = ['*-12-25', '*-01-01', '2013-12-23']; # variable and fixed holidays

    $from = new DateTime($from);
    $to = new DateTime($to);
	$to->modify('+1 day');
    $interval = new DateInterval('P1D');
    $periods = new DatePeriod($from, $interval, $to);
	
	$days = 0;
	foreach ($periods as $period) {
        if (!in_array($period->format('N'), $workingDays)) continue;
        if (in_array($period->format('Y-m-d'), $holidayDays)) continue;
        if (in_array($period->format('*-m-d'), $holidayDays)) continue;
		$days++;
	}
	return $days;
}

Solution 3 - Php

There are some args for the date() function that should help. If you check date("w") it will give you a number for the day of the week, from 0 for Sunday through 6 for Saturday. So.. maybe something like..

$busDays = 3;
$day = date("w");
if( $day > 2 && $day <= 5 ) { /* if between Wed and Fri */
  $day += 2; /* add 2 more days for weekend */
}
$day += $busDays;

This is just a rough example of one possibility..

Solution 4 - Php

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

Solution 5 - Php

$startDate = new DateTime( '2013-04-01' );    //intialize start date
$endDate = new DateTime( '2013-04-30' );    //initialize end date
$holiday = array('2013-04-11','2013-04-25');  //this is assumed list of holiday
$interval = new DateInterval('P1D');    // set the interval as 1 day
$daterange = new DatePeriod($startDate, $interval ,$endDate);
foreach($daterange as $date){
if($date->format("N") <6 AND !in_array($date->format("Y-m-d"),$holiday))
$result[] = $date->format("Y-m-d");
}
echo "<pre>";print_r($result);

Solution 6 - Php

Here is a function for adding buisness days to a date

 function add_business_days($startdate,$buisnessdays,$holidays,$dateformat){
  $i=1;
  $dayx = strtotime($startdate);
  while($i < $buisnessdays){
   $day = date('N',$dayx);
   $date = date('Y-m-d',$dayx);
   if($day < 6 && !in_array($date,$holidays))$i++;
   $dayx = strtotime($date.' +1 day');
  }
  return date($dateformat,$dayx);
 }
 
 //Example
 date_default_timezone_set('Europe\London');
 $startdate = '2012-01-08';
 $holidays=array("2012-01-10");
 echo '<p>Start date: '.date('r',strtotime( $startdate));
 echo '<p>'.add_business_days($startdate,7,$holidays,'r');

Another post mentions getWorkingDays (from php.net comments and included here) but I think it breaks if you start on a Sunday and finish on a work day.

Using the following (you'll need to include the getWorkingDays function from previous post)

 date_default_timezone_set('Europe\London');
 //Example:
 $holidays = array('2012-01-10');
 $startDate = '2012-01-08';
 $endDate = '2012-01-13';
 echo getWorkingDays( $startDate,$endDate,$holidays);

Gives the result as 5 not 4

Sun, 08 Jan 2012 00:00:00 +0000 weekend
Mon, 09 Jan 2012 00:00:00 +0000
Tue, 10 Jan 2012 00:00:00 +0000 holiday
Wed, 11 Jan 2012 00:00:00 +0000
Thu, 12 Jan 2012 00:00:00 +0000
Fri, 13 Jan 2012 00:00:00 +0000 

The following function was used to generate the above.

     function get_working_days($startDate,$endDate,$holidays){
      $debug = true;
      $work = 0;
      $nowork = 0;
      $dayx = strtotime($startDate);
      $endx = strtotime($endDate);
      if($debug){
       echo '<h1>get_working_days</h1>';
       echo 'startDate: '.date('r',strtotime( $startDate)).'<br>';
       echo 'endDate: '.date('r',strtotime( $endDate)).'<br>';
       var_dump($holidays);
       echo '<p>Go to work...';
      }
      while($dayx <= $endx){
       $day = date('N',$dayx);
       $date = date('Y-m-d',$dayx);
       if($debug)echo '<br />'.date('r',$dayx).' ';
       if($day > 5 || in_array($date,$holidays)){
        $nowork++;
     if($debug){
      if($day > 5)echo 'weekend';
      else echo 'holiday';
     }
       } else $work++;
       $dayx = strtotime($date.' +1 day');
      }
      if($debug){
      echo '<p>No work: '.$nowork.'<br>';
      echo 'Work: '.$work.'<br>';
      echo 'Work + no work: '.($nowork+$work).'<br>';
      echo 'All seconds / seconds in a day: '.floatval(strtotime($endDate)-strtotime($startDate))/floatval(24*60*60);
      }
      return $work;
     }

    date_default_timezone_set('Europe\London');
     //Example:
     $holidays=array("2012-01-10");
     $startDate = '2012-01-08';
     $endDate = '2012-01-13';
//broken
     echo getWorkingDays( $startDate,$endDate,$holidays);
//works
     echo get_working_days( $startDate,$endDate,$holidays);

Bring on the holidays...

Solution 7 - Php

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

Solution 8 - Php

My version based on the work by @mcgrailm... tweaked because the report needed to be reviewed within 3 business days, and if submitted on a weekend, the counting would start on the following Monday:

function business_days_add($start_date, $business_days, $holidays = array()) {
	$current_date = strtotime($start_date);
	$business_days = intval($business_days); // Decrement does not work on strings
	while ($business_days > 0) {
		if (date('N', $current_date) < 6 && !in_array(date('Y-m-d', $current_date), $holidays)) {
			$business_days--;
		}
		if ($business_days > 0) {
			$current_date = strtotime('+1 day', $current_date);
		}
	}
	return $current_date;
}

And working out the difference of two dates in terms of business days:

function business_days_diff($start_date, $end_date, $holidays = array()) {
	$business_days = 0;
	$current_date = strtotime($start_date);
	$end_date = strtotime($end_date);
	while ($current_date <= $end_date) {
		if (date('N', $current_date) < 6 && !in_array(date('Y-m-d', $current_date), $holidays)) {
			$business_days++;
		}
		if ($current_date <= $end_date) {
			$current_date = strtotime('+1 day', $current_date);
		}
	}
	return $business_days;
}

As a note, everyone who is using 86400, or 246060, please don't... your forgetting time changes from winter/summer time, where a day it not exactly 24 hours. While it's a little slower the strtotime('+1 day', $timestamp), it much more reliable.

Solution 9 - Php

A function to add or subtract business days from a given date, this doesn't account for holidays.

function dateFromBusinessDays($days, $dateTime=null) {
  $dateTime = is_null($dateTime) ? time() : $dateTime;
  $_day = 0;
  $_direction = $days == 0 ? 0 : intval($days/abs($days));
  $_day_value = (60 * 60 * 24);

  while($_day !== $days) {
    $dateTime += $_direction * $_day_value;

    $_day_w = date("w", $dateTime);
    if ($_day_w > 0 && $_day_w < 6) {
      $_day += $_direction * 1; 
    }
  }

  return $dateTime;
}

use like so...

echo date("m/d/Y", dateFromBusinessDays(-7));
echo date("m/d/Y", dateFromBusinessDays(3, time() + 3*60*60*24));

Solution 10 - Php

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

Solution 11 - Php

Below is the working code to calculate working business days from a given date.

<?php
$holiday_date_array = array("2016-01-26", "2016-03-07", "2016-03-24", "2016-03-25", "2016-04-15", "2016-08-15", "2016-09-12", "2016-10-11", "2016-10-31");
$date_required = "2016-03-01";

function increase_date($date_required, $holiday_date_array=array(), $days = 15){
	if(!empty($date_required)){
		$counter_1=0;
		$incremented_date = '';
		for($i=1; $i <= $days; $i++){
			$date = strtotime("+$i day", strtotime($date_required));
			$day_name = date("D", $date);
			$incremented_date = date("Y-m-d", $date);
			if($day_name=='Sat'||$day_name=='Sun'|| in_array($incremented_date ,$holiday_date_array)==true){
				$counter_1+=1;
			}
		}
		if($counter_1 > 0){
			return increase_date($incremented_date, $holiday_date_array, $counter_1);
		}else{
			return $incremented_date;
		}
	}else{
		return 'invalid';
	}
}

echo increase_date($date_required, $holiday_date_array, 15);
?>

//output after adding 15 business working days in 2016-03-01 will be "2016-03-23"

Solution 12 - Php

This code snippet is very easy to calculate business day without week end and holidays:

function getWorkingDays($startDate,$endDate,$offdays,$holidays){
$endDate = strtotime($endDate);
$startDate = strtotime($startDate);
$days = ($endDate - $startDate) / 86400 + 1;
$counter=0;
for ($i = 1; $i <= $days; $i++) {
	$the_first_day_of_week = date("N", $startDate);
	$startDate+=86400;
if (!in_array($the_first_day_of_week, $offdays) && !in_array(date("Y-m-
d",$startDate), $holidays)) {
$counter++;
}
	
}   
return $counter;
}
//example to use
$holidays=array("2017-07-03","2017-07-20");
$offdays=array(5,6);//weekend days Monday=1 .... Sunday=7
echo getWorkingDays("2017-01-01","2017-12-31",$offdays,$holidays)

Solution 13 - Php

Here is another solution without for loop for each day.

$from = new DateTime($first_date);
$to = new DateTime($second_date);

$to->modify('+1 day');
$interval = $from->diff($to);
$days = $interval->format('%a');
           
$extra_days = fmod($days, 7);
$workdays = ( ( $days - $extra_days ) / 7 ) * 5;
           
$first_day = date('N', strtotime($first_date));
$last_day = date('N', strtotime("1 day", strtotime($second_date)));
$extra = 0;
if($first_day > $last_day) {
   if($first_day == 7) {
       $first_day = 6;
   }
                
   $extra = (6 - $first_day) + ($last_day - 1);
   if($extra < 0) {
       $extra = $extra * -1;
   }
}
if($last_day > $first_day) {
    $extra = $last_day - $first_day;
}
$days = $workdays + $extra

Solution 14 - Php

For holidays, make an array of days in some format that date() can produce. Example:

// I know, these aren't holidays
$holidays = array(
    'Jan 2',
    'Feb 3',
    'Mar 5',
    'Apr 7',
    // ...
);

Then use the in_array() and date() functions to check if the timestamp represents a holiday:

$day_of_year = date('M j', $timestamp);
$is_holiday = in_array($day_of_year, $holidays);

Solution 15 - Php

I had this same need i started with bobbin's first example and ended up with this

  function add_business_days($startdate,$buisnessdays,$holidays=array(),$dateformat){
  	$enddate = strtotime($startdate);
  	$day = date('N',$enddate);
  	while($buisnessdays > 1){
  		$enddate = strtotime(date('Y-m-d',$enddate).' +1 day');
  		$day = date('N',$enddate);
  		if($day < 6 && !in_array($enddate,$holidays))$buisnessdays--;
  	}
  	return date($dateformat,$enddate);
  }

hth someone

Solution 16 - Php

<?php
// $today is the UNIX timestamp for today's date
$today = time();
echo "<strong>Today is (ORDER DATE): " . '<font color="red">' . date('l, F j, Y', $today) . "</font></strong><br/><br/>";

//The numerical representation for day of week (Ex. 01 for Monday .... 07 for Sunday
$today_numerical = date("N",$today);

//leadtime_days holds the numeric value for the number of business days 
$leadtime_days = $_POST["leadtime"];

//leadtime is the adjusted date for shipdate
$shipdate = time();

while ($leadtime_days > 0) 
{
 if ($today_numerical != 5 && $today_numerical != 6)
 {
  $shipdate = $shipdate + (60*60*24);
  $today_numerical = date("N",$shipdate);
  $leadtime_days --;
 }
 else
  $shipdate = $shipdate + (60*60*24);
  $today_numerical = date("N",$shipdate);
}

echo '<strong>Estimated Ship date: ' . '<font color="green">' . date('l, F j, Y', $shipdate) . "</font></strong>";
?>

Solution 17 - Php

Variant 1:

<?php
/*
 * Does not count current day, the date returned is the last business day
 * Requires PHP 5.1 (Using ISO-8601 week)
 */

function businessDays($timestamp = false, $bDays = 2) {
    if($timestamp === false) $timestamp = time();
    while ($bDays>0) {
        $timestamp += 86400;
        if (date('N', $timestamp)<6) $bDays--;
    }
    return $timestamp;
}

Variant 2:

<?php
/*
 * Does not count current day, the date returned is a business day 
 * following the last business day
 * Requires PHP 5.1 (Using ISO-8601 week)
 */

function businessDays($timestamp = false, $bDays = 2) {
    if($timestamp === false) $timestamp = time();
    while ($bDays+1>0) {
        $timestamp += 86400;
        if (date('N', $timestamp)<6) $bDays--;
    }
    return $timestamp;
}

Variant 3:

<?php
/*
 * Does not count current day, the date returned is 
 * a date following the last business day (can be weekend or not. 
 * See above for alternatives)
 * Requires PHP 5.1 (Using ISO-8601 week)
 */

function businessDays($timestamp = false, $bDays = 2) {
    if($timestamp === false) $timestamp = time();
    while ($bDays>0) {
        $timestamp += 86400;
        if (date('N', $timestamp)<6) $bDays--;
    }
    return $timestamp += 86400;
}

The additional holiday considerations can be made using variations of the above by doing the following. Note! assure all the timestamps are the same time of the day (i.e. midnight).

Make an array of holiday dates (as unixtimestamps) i.e.:

$holidays = array_flip(strtotime('2011-01-01'),strtotime('2011-12-25'));

Modify line :

if (date('N', $timestamp)<6) $bDays--;

to be :

if (date('N', $timestamp)<6 && !isset($holidays[$timestamp])) $bDays--;

Done!

<?php
/*
 * Does not count current day, the date returned is the last business day
 * Requires PHP 5.1 (Using ISO-8601 week)
 */

function businessDays($timestamp = false, $bDays = 2) {
    if($timestamp === false) $timestamp = strtotime(date('Y-m-d',time()));
    $holidays = array_flip(strtotime('2011-01-01'),strtotime('2011-12-25'));
    while ($bDays>0) {
        $timestamp += 86400;
        if (date('N', $timestamp)<6 && !isset($holidays[$timestamp])) $bDays--;
    }
    return $timestamp;
}

Solution 18 - Php

<?php 
function AddWorkDays(){
$i = 0;
$d = 5; // Number of days to add

	while($i <= $d) {
	$i++;
		if(date('N', mktime(0, 0, 0, date(m), date(d)+$i, date(Y))) < 5) {
			$d++;
		}
	}
	return date(Y).','.date(m).','.(date(d)+$d);
}
?>

Solution 19 - Php

Here is a recursive solution. It can easily be modified to only keep track of and return the latest date.

//	Returns a $numBusDays-sized array of all business dates, 
//  starting from and including $currentDate. 
//  Any date in $holidays will be skipped over.

function getWorkingDays($currentDate, $numBusDays, $holidays = array(), 
  $resultDates = array())
{
  //  exit when we have collected the required number of business days
  if ($numBusDays === 0) {
    return $resultDates;
  }
  
  //  add current date to return array, if not a weekend or holiday
  $date = date("w", strtotime($currentDate));
  if ( $date != 0  &&  $date != 6  &&  !in_array($currentDate, $holidays) ) {
    $resultDates[] = $currentDate;
    $numBusDays -= 1;
  }

  //  set up the next date to test
  $currentDate = new DateTime("$currentDate + 1 day");
  $currentDate = $currentDate->format('Y-m-d');

  return getWorkingDays($currentDate, $numBusDays, $holidays, $resultDates);
}

//	test
$days = getWorkingDays('2008-12-05', 4);
print_r($days);

Solution 20 - Php

date_default_timezone_set('America/New_York');


/** Given a number days out, what day is that when counting by 'business' days
  * get the next business day. by default it looks for next business day
  * ie calling 	$date = get_next_busines_day(); on monday will return tuesday
  * 			$date = get_next_busines_day(2); on monday will return wednesday
  * 			$date = get_next_busines_day(2); on friday will return tuesday
  *
  * @param $number_of_business_days (integer)	 	how many business days out do you want
  * @param $start_date (string) 					strtotime parseable time value
  * @param $ignore_holidays (boolean)				true/false to ignore holidays
  * @param $return_format (string)					as specified in php.net/date
 */
function get_next_business_day($number_of_business_days=1,$start_date='today',$ignore_holidays=false,$return_format='m/d/y') {
	
	// get the start date as a string to time
	$result = strtotime($start_date);
	
	// now keep adding to today's date until number of business days is 0 and we land on a business day
	while ($number_of_business_days > 0) {
		// add one day to the start date
		$result = strtotime(date('Y-m-d',$result) . " + 1 day");
		
		// this day counts if it's a weekend and not a holiday, or if we choose to ignore holidays
		if (is_weekday(date('Y-m-d',$result)) && (!(is_holiday(date('Y-m-d',$result))) || $ignore_holidays) ) 
			$number_of_business_days--;
		
	}
	
	// when my $number of business days is exausted I have my final date
	
	return(date($return_format,$result));
}

    function is_weekend($date) {
	// return if this is a weekend date or not.
	return (date('N', strtotime($date)) >= 6);
}

function is_weekday($date) {
	// return if this is a weekend date or not.
	return (date('N', strtotime($date)) < 6);
}

function is_holiday($date) {
	// return if this is a holiday or not.

	// what are my holidays for this year
	$holidays = array("New Year's Day 2011" => "12/31/10",
						"Good Friday" => "04/06/12",
						"Memorial Day" => "05/28/12",
						"Independence Day" => "07/04/12",
						"Floating Holiday" => "12/31/12",
						"Labor Day" => "09/03/12",
						"Thanksgiving Day" => "11/22/12",
						"Day After Thanksgiving Day" => "11/23/12",
						"Christmas Eve" => "12/24/12",
						"Christmas Day" => "12/25/12",
						"New Year's Day 2012" => "01/02/12",
						"New Year's Day 2013" => "01/01/13"
						);
						
	return(in_array(date('m/d/y', strtotime($date)),$holidays));
}


print get_next_business_day(1) . "\n";

Solution 21 - Php

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:

  1. need to specify what weekdays you'll be working (default to MON-FRI) - the class allows you to enable or disable each weekday individually.
  2. need to know that you need to consider public holidays (country and state) to be accurate

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;

Solution 22 - Php

This is another solution, it is nearly 25% faster than checking holidays with in_array:

/**
 * Function to calculate the working days between two days, considering holidays.
 * @param string $startDate -- Start date of the range (included), formatted as Y-m-d.
 * @param string $endDate -- End date of the range (included), formatted as Y-m-d.
 * @param array(string) $holidayDates -- OPTIONAL. Array of holidays dates, formatted as Y-m-d. (e.g. array("2016-08-15", "2016-12-25"))
 * @return int -- Number of working days.
 */
function getWorkingDays($startDate, $endDate, $holidayDates=array()){
	$dateRange = new DatePeriod(new DateTime($startDate), new DateInterval('P1D'), (new DateTime($endDate))->modify("+1day"));
	foreach ($dateRange as $dr) { if($dr->format("N")<6){$workingDays[]=$dr->format("Y-m-d");} }
	return count(array_diff($workingDays, $holidayDates));
}

Solution 23 - Php

I know I'm late to the party, but I use this old set of functions by Marcos J. Montes for figuring out holidays and business days. He took the time to add an algorithm from 1876 for Easter and he added all the major US holidays. This can easily be updated for other countries.

//Usage
$days = 30;
$next_working_date = nextWorkingDay($days, $somedate);

//add date function
function DateAdd($interval, $number, $date) {

    $date_time_array = getdate($date);
	//die(print_r($date_time_array));

    $hours = $date_time_array["hours"];
    $minutes = $date_time_array["minutes"];
    $seconds = $date_time_array["seconds"];
    $month = $date_time_array["mon"];
    $day = $date_time_array["mday"];
    $year = $date_time_array["year"];

    switch ($interval) {

        case "yyyy":
            $year+=$number;
            break;
        case "q":
            $year+=($number*3);
            break;
        case "m":
            $month+=$number;
            break;
        case "y":
        case "d":
        case "w":
            $day+=$number;
            break;
        case "ww":
            $day+=($number*7);
            break;
        case "h":
            $hours+=$number;
            break;
        case "n":
            $minutes+=$number;
            break;
        case "s":
            $seconds+=$number; 
            break;            
    }
    //		echo "day:" . $day;
    $timestamp= mktime($hours,$minutes,$seconds,$month,$day,$year);
    return $timestamp;
}

// the following function get_holiday() is based on the work done by
// Marcos J. Montes
function get_holiday($year, $month, $day_of_week, $week="") {
    if ( (($week != "") && (($week > 5) || ($week < 1))) || ($day_of_week > 6) || ($day_of_week < 0) ) {
        // $day_of_week must be between 0 and 6 (Sun=0, ... Sat=6); $week must be between 1 and 5
        return FALSE;
    } else {
        if (!$week || ($week == "")) {
            $lastday = date("t", mktime(0,0,0,$month,1,$year));
            $temp = (date("w",mktime(0,0,0,$month,$lastday,$year)) - $day_of_week) % 7;
        } else {
            $temp = ($day_of_week - date("w",mktime(0,0,0,$month,1,$year))) % 7;
        }
    
        if ($temp < 0) {
            $temp += 7;
        }

        if (!$week || ($week == "")) {
            $day = $lastday - $temp;
        } else {
            $day = (7 * $week) - 6 + $temp;
        }
	    //echo $year.", ".$month.", ".$day . "<br><br>";
        return format_date($year, $month, $day);
    }
}

function observed_day($year, $month, $day) {
    // sat -> fri & sun -> mon, any exceptions?
    //
    // should check $lastday for bumping forward and $firstday for bumping back,
    // although New Year's & Easter look to be the only holidays that potentially
    // move to a different month, and both are accounted for.

    $dow = date("w", mktime(0, 0, 0, $month, $day, $year));

    if ($dow == 0) {
        $dow = $day + 1;
    } elseif ($dow == 6) {
        if (($month == 1) && ($day == 1)) {    // New Year's on a Saturday
            $year--;
            $month = 12;
            $dow = 31;
        } else {
            $dow = $day - 1;
        }
    } else {
        $dow = $day;
    }

    return format_date($year, $month, $dow);
}

function calculate_easter($y) {
    // In the text below, 'intval($var1/$var2)' represents an integer division neglecting
    // the remainder, while % is division keeping only the remainder. So 30/7=4, and 30%7=2
//
    // This algorithm is from Practical Astronomy With Your Calculator, 2nd Edition by Peter
    // Duffett-Smith. It was originally from Butcher's Ecclesiastical Calendar, published in
    // 1876. This algorithm has also been published in the 1922 book General Astronomy by
    // Spencer Jones; in The Journal of the British Astronomical Association (Vol.88, page
    // 91, December 1977); and in Astronomical Algorithms (1991) by Jean Meeus. 

    $a = $y%19;
    $b = intval($y/100);
    $c = $y%100;
    $d = intval($b/4);
    $e = $b%4;
    $f = intval(($b+8)/25);
    $g = intval(($b-$f+1)/3);
    $h = (19*$a+$b-$d-$g+15)%30;
    $i = intval($c/4);
    $k = $c%4;
    $l = (32+2*$e+2*$i-$h-$k)%7;
    $m = intval(($a+11*$h+22*$l)/451);
    $p = ($h+$l-7*$m+114)%31;
    $EasterMonth = intval(($h+$l-7*$m+114)/31);    // [3 = March, 4 = April]
    $EasterDay = $p+1;    // (day in Easter Month)

    return format_date($y, $EasterMonth, $EasterDay);
}


function nextWorkingDay($number_days, $start_date = "") {
    $day_counter = 0;
    $intCounter = 0;	

    if ($start_date=="") {
	    $today  = mktime(0, 0, 0, date("m")  , date("d"), date("Y"));
    } else {
	    $start_time = strtotime($start_date);
	    $today  = mktime(0, 0, 0, date("m", $start_time)  , date("d", $start_time), date("Y", $start_time));
    }

    while($day_counter < $number_days) {
	    $working_time = DateAdd("d", 1, $today);
	    $working_date = date("Y-m-d", $working_date);
	    if (!isWeekend($working_date) && !confirm_holiday(date("Y-m-d", strtotime($working_date))) ) {
		    $day_counter++;
	    }
	    $intCounter++;
	    $today  = $working_time;
	    if ($intCounter > 1000) {
		    //just in case out of control?
		    break;
	    }
    }

    return $working_date;
}
function isWeekend($check_date) {
    return (date("N",  strtotime($check_date)) > 5);
}
function confirm_holiday($somedate="") {
    if ($somedate=="") {
	    $somedate = date("Y-m-d");
    }
    $year = date("Y", strtotime($somedate));
    $blnHoliday = false;
    //newyears
    if ($somedate == observed_day($year, 1, 1)) {
    	$blnHoliday = true;
    }
    if ($somedate == format_date($year, 1, 1)) {
	    $blnHoliday = true;
    }
    if ($somedate == format_date($year, 12, 31)) {
	    $blnHoliday = true;
    }
    //Martin Luther King
    if ($somedate == get_holiday($year, 1, 1, 3)) {
	    $blnHoliday = true;
    }
    //President's
    if ($somedate == get_holiday($year, 2, 1, 3)) {
	    $blnHoliday = true;
    }
    //easter
    if ($somedate == calculate_easter($year)) {
	    $blnHoliday = true;
    }
    //Memorial
    if ($somedate == get_holiday($year, 5, 1)) {
	    $blnHoliday = true;
    }
    //july4
    if ($somedate == observed_day($year, 7, 4)) {
	    $blnHoliday = true;
    }
    //labor
    if ($somedate == get_holiday($year, 9, 1, 1)) {
	    $blnHoliday = true;
    }
    //columbus
    if ($somedate == get_holiday($year, 10, 1, 2)) {
	    $blnHoliday = true;
    }
    //thanks
    if ($somedate == get_holiday($year, 11, 4, 4)) {
	    $blnHoliday = true;
    }
    //xmas
    if ($somedate == format_date($year, 12, 24)) {
	    $blnHoliday = true;
    }
    if ($somedate == format_date($year, 12, 25)) {
	    $blnHoliday = true;
    }
    return $blnHoliday;
}

Solution 24 - Php

function get_business_days_forward_from_date($num_days, $start_date='', $rtn_fmt='Y-m-d') {

// $start_date will default to today    
 
if ($start_date=='') { $start_date = date("Y-m-d"); }

$business_day_ct = 0;

$max_days = 10000 + $num_days;  // to avoid any possibility of an infinite loop


// define holidays, this currently only goes to 2012 because, well, you know... ;-)
// if the world is still here after that, you can find more at
// http://www.opm.gov/Operating_Status_Schedules/fedhol/2013.asp
// always add holidays in order, because the iteration will stop when the holiday is > date being tested

$fed_holidays=array(
    "2010-01-01",
    "2010-01-18",
    "2010-02-15",
    "2010-05-31",
    "2010-07-05",
    "2010-09-06",
    "2010-10-11",
    "2010-11-11",
    "2010-11-25",
    "2010-12-24",
    
    "2010-12-31",
    "2011-01-17",
    "2011-02-21",
    "2011-05-30",
    "2011-07-04",
    "2011-09-05",
    "2011-10-10",
    "2011-11-11",
    "2011-11-24",
    "2011-12-26",

    "2012-01-02",
    "2012-01-16",
    "2012-02-20",
    "2012-05-28",
    "2012-07-04",
    "2012-09-03",
    "2012-10-08",
    "2012-11-12",
    "2012-11-22",
    "2012-12-25",
    );

$curr_date_ymd = date('Y-m-d', strtotime($start_date));    

for ($x=1;$x<$max_days;$x++)
{
    if (intval($num_days)==intval($business_day_ct)) { return(date($rtn_fmt, strtotime($curr_date_ymd))); }  // date found - return
    
    // get next day to check

    $curr_date_ymd = date('Y-m-d', (strtotime($start_date)+($x * 86400)));   // add 1 day to the current date
    
    $is_business_day = 1;
    
    // check if this is a weekend   1 (for Monday) through 7 (for Sunday)
    
    if ( intval(date("N",strtotime($curr_date_ymd))) > 5) { $is_business_day = 0; }

    //check for holiday
    foreach($fed_holidays as $holiday)
    {
        if (strtotime($holiday)==strtotime($curr_date_ymd))  // holiday found
        {
            $is_business_day = 0;
            break 1;
        }

        if (strtotime($holiday)>strtotime($curr_date_ymd)) { break 1; }  // past date, stop searching (always add holidays in order)
        

    }
    
    $business_day_ct = $business_day_ct + $is_business_day;  // increment if this is a business day
 
} 
 
// if we get here, you are hosed
return ("ERROR");
 

}

Solution 25 - Php

The add_business_days has a small bug. Try the following with the existing function and the output will be a Saturday.

Startdate = Friday Business days to add = 1 Holidays array = Add date for the following Monday.

I have fixed that in my function below.

function add_business_days($startdate, $buisnessdays, $holidays = array(), $dateformat = 'Y-m-d'){
$i= 1;
$dayx= strtotime($startdate);
$buisnessdays= ceil($buisnessdays);

while($i < $buisnessdays)
{
	$day= date('N',$dayx);

	$date= date('Y-m-d',$dayx);
	if($day < 6 && !in_array($date,$holidays))
		$i++;

	$dayx= strtotime($date.' +1 day');
}

## If the calculated day falls on a weekend or is a holiday, then add days to the next business day
$day= date('N',$dayx);
$date= date('Y-m-d',$dayx);

while($day >= 6 || in_array($date,$holidays))
{
	$dayx= strtotime($date.' +1 day');
	$day= date('N',$dayx);
	$date= date('Y-m-d',$dayx);
}

return date($dateformat, $dayx);}

Solution 26 - Php

Thanks to Bobbin, mcgrailm, Tony, James Pasta and a few others who posted here. I had written my own function to add business days to a date, but modified it with some code I found here. This will handle the start date being on a weekend/holiday. This will also handle business hours. I added some comments and break up the code to make it easier to read.

<?php
function count_business_days($date, $days, $holidays) {
    $date = strtotime($date);
	
	for ($i = 1; $i <= intval($days); $i++) { //Loops each day count
	
		//First, find the next available weekday because this might be a weekend/holiday
        while (date('N', $date) >= 6 || in_array(date('Y-m-d', $date), $holidays)){
            $date = strtotime(date('Y-m-d',$date).' +1 day');
		}
		
		//Now that we know we have a business day, add 1 day to it
		$date = strtotime(date('Y-m-d',$date).' +1 day');
		
		//If this day that was previously added falls on a weekend/holiday, then find the next business day
        while (date('N', $date) >= 6 || in_array(date('Y-m-d', $date), $holidays)){
            $date = strtotime(date('Y-m-d',$date).' +1 day');
		}
	}
    return date('Y-m-d', $date);
}

//Also add in the code from Tony and James Pasta to handle holidays...

function getNationalAmericanHolidays($year) {
$bankHolidays = array(
    'New Years Day' => $year . "-01-01",
    'Martin Luther King Jr Birthday' => "". date("Y-m-d",strtotime("third Monday of January " . $year) ),
    'Washingtons Birthday' => "". date("Y-m-d",strtotime("third Monday of February " . $year) ),
    'Memorial Day' => "". date("Y-m-d",strtotime("last Monday of May " . $year) ),
    'Independance Day' => $year . "-07-04",
    'Labor Day' => "". date("Y-m-d",strtotime("first Monday of September " . $year) ),
    'Columbus Day' => "". date("Y-m-d",strtotime("second Monday of October " . $year) ),
    'Veterans Day' => $year . "-11-11",
    'Thanksgiving Day' => "". date("Y-m-d",strtotime("fourth Thursday of November " . $year) ),
    'Christmas Day' => $year . "-12-25"
);
return $bankHolidays;

}

//Now to call it... since we're working with business days, we should
//also be working with business hours so check if it's after 5 PM
//and go to the next day if necessary.

//Go to next day if after 5 pm (5 pm = 17)
if (date(G) >= 17) {
	$start_date = date("Y-m-d", strtotime("+ 1 day")); //Tomorrow
} else {
	$start_date = date("Y-m-d"); //Today
}

//Get the holidays for the current year and also for the next year
$this_year = getNationalAmericanHolidays(date('Y'));
$next_year = getNationalAmericanHolidays(date('Y', strtotime("+12 months")));
$holidays = array_merge($this_year, $next_year);

//The number of days to count
$days_count = 10;

echo count_business_days($start_date, $days_count, $holidays);

?>

Solution 27 - Php

Personally, I think this is a cleaner and more concise solution:

function onlyWorkDays( $d ) {
	$holidays = array('2013-12-25','2013-12-31','2014-01-01','2014-01-20','2014-02-17','2014-05-26','2014-07-04','2014-09-01','2014-10-13','2014-11-11','2014-11-27','2014-12-25','2014-12-31');
	while (in_array($d->format("Y-m-d"), $holidays)) { // HOLIDAYS
		$d->sub(new DateInterval("P1D"));
	}
	if ($d->format("w") == 6) { // SATURDAY
		$d->sub(new DateInterval("P1D"));
	}
	if ($d->format("w") == 0) { // SUNDAY
		$d->sub(new DateInterval("P2D"));
	}
	return $d;
}

Just send the proposed new date to this function.

Solution 28 - Php

I just created this function, which seems to work very well:

function getBusinessDays($date1, $date2){

    if(!is_numeric($date1)){
        $date1 = strtotime($date1);
    }

    if(!is_numeric($date2)){
        $date2 = strtotime($date2);
    }

    if($date2 < $date1){
        $temp_date = $date1;
        $date1 = $date2;
        $date2 = $temp_date;
        unset($temp_date);
    }

    $diff = $date2 - $date1;

    $days_diff = intval($diff / (3600 * 24));
    $current_day_of_week = intval(date("N", $date1));
    $business_days = 0;

    for($i = 1; $i <= $days_diff; $i++){
        if(!in_array($current_day_of_week, array("Sunday" => 1, "Saturday" => 7))){
            $business_days++;
        }

        $current_day_of_week++;
        if($current_day_of_week > 7){
            $current_day_of_week = 1;
        }
    }

    return $business_days;
}

echo "Business days: " . getBusinessDays("8/15/2014", "8/8/2014");

Solution 29 - Php

If you need to get how much time was between two dates you can use https://github.com/maximnara/business-days-counter. It's works simply but only with laravel now $diffInSeconds = $this->datesCounter->getDifferenceInSeconds(Carbon::create(2019, 1, 1), Carbon::now(), DateCounter::COUNTRY_FR);

It doesn't counts public holidays and weekends and you can set working interval, for example from 9 to 18 with launch hour or no.

Or if you need just weekends and you use Carbon you can use built in function:

$date1 = Carbon::create(2019, 1, 1)->endOfDay();
$date2 = $dt->copy()->startOfDay();
$diff = $date1->diffFiltered(CarbonInterval::minute(), function(Carbon $date) {
   return !$date->isWeekend();
}, $date2, true);

But it will foreach every minute in interval, for bit intervals it can take a while.

Solution 30 - Php

Although this question has over 35 answers at the time of writing, there is a better solution to this problem.

@flamingLogos provided an answer which is based on Mathematics and doesn't contain any loops. That solution provides a time complexity of Θ(1). However, the math behind it is pretty complex especially the leap year handling.

@Glavić provided a good solution that is minimal and elegant. But it doesn't preform the calculation in a constant time, so it could produce a denial of service (DOS) attack or at least timeout if used with large periods like 10 or 100 of years since it loops on 1 day interval.

So I propose a mathematical approach that have constant time and yet very readable.

The idea is to count how many days to have complete weeks.

<?php
function getWorkingHours($start_date, $end_date) {
    // validate input
    if(!validateDate($start_date) || !validateDate($end_date)) return ['error' => 'Invalid Date'];
    if($end_date < $start_date) return ['error' => 'End date must be greater than or equal Start date'];

    //We save timezone and switch to UTC to prevent issues
    $old_timezone = date_default_timezone_get();
    date_default_timezone_set("UTC");

    $startDate = strtotime($start_date);
    $endDate = strtotime($end_date);

    //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 include both dates in the interval.
    $days = ($endDate - $startDate) / 86400 + 1;
    $no_full_weeks = ceil($days / 7);
    //we get only missing days count to complete full weeks
    //we take modulo 7 in case it was already full weeks
    $no_of_missing_days = (7 - ($days % 7)) % 7;

    $workingDays = $no_full_weeks * 5;
    //Next we remove the working days we added, this loop will have max of 6 iterations.
    for ($i = 1; $i <= $no_of_missing_days; $i++){
        if(date('N', $endDate + $i * 86400) < 6) $workingDays--;
    }

    $holidays = getHolidays(date('Y', $startDate), date('Y', $endDate));

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

    date_default_timezone_set($old_timezone);
    return ['days' => $workingDays];

}

The input to the function are in the format of Y-m-d in php or yyyy-mm-dd in general date format.

The get holiday function will return an array of holiday dates starting from start year and until end year.

Solution 31 - Php

I stumbled upon another question that asked a similar question and started writing code that solved his problem, but then found this question. Since I already have the code, I figure it won't be of any harm to post it here, someone will probably find it useful. I believe it is better/more flexible than most answers to this question.

My class is available here: https://gist.github.com/jurchiks/62ee8c4267dde48a296a8c2ab18965df

And its usage is as follows:

use Cake\Chronos\Date;

$weekdayCalculator = new WeekdayCalculator();
$weekdayCalculator->addSpecialWorkday(new Date('2022-01-08')); // Override Saturday as a workday.
// Sunday stays a normal holiday as usual.
$weekdayCalculator->addSpecialHoliday(new Date('2022-01-10')); // Override the following Monday as a holiday.
// As a result, we've shifted the workday one day back.

var_dump(
	$weekdayCalculator->isWorkday(new Date('2022-01-08')), // Returns TRUE - we manually set this date as a workday.
	$weekdayCalculator->isHoliday(new Date('2022-01-09')), // Returns TRUE - normal holiday.
	$weekdayCalculator->getNextWorkday(new Date('2022-01-07')), // Returns Date(2022-01-08).
	$weekdayCalculator->getNextWorkday(new Date('2022-01-08')), // Returns Date(2022-01-11) - 9, 10 = holidays, 11 = normal workday.
	$weekdayCalculator->getNextHoliday(new Date('2022-01-08')), // Returns Date(2022-01-09).
	$weekdayCalculator->countDays(new Date('2022-01-01'), new Date('2022-02-01')), // Returns int(31) - January has 31 days, 02-01 is excluded.
	$weekdayCalculator->countWorkdays(new Date('2022-01-01'), new Date('2022-02-01')), // Returns int(21).
	$weekdayCalculator->countHolidays(new Date('2022-01-01'), new Date('2022-02-01')), // Returns int(10).
	$weekdayCalculator->getDays(new Date('2022-01-01'), new Date('2022-02-01')), // Returns a Generator instance for all days between specified dates.
	$weekdayCalculator->getWorkdays(new Date('2022-01-01'), new Date('2022-02-01')), // Returns a Generator instance for all workdays.
	$weekdayCalculator->getHolidays(new Date('2022-01-01'), new Date('2022-02-01')), // Returns a Generator instance for all holidays.
	iterator_to_array($weekdayCalculator->getWorkdays(new Date('2022-01-01'), new Date('2022-02-01'))), // Returns an array of 21 Date instances.
);

You will still have to write your own code to add your country's specific work/holidays, but there are examples of how to do that in other answers here, and besides, that is not quite in the scope of a class like this.

Yes, it is dependent on cakephp/chronos because it's a very nice immutable-first PHP DateTime alternative; I much prefer it to Carbon, which is mutable-first.

Solution 32 - Php

An enhancement to the function offered by James Pasta above, to include all Federal Holidays, and to correct 4th July (was calculated as 4th June above!), and to also include the holiday name as the array key...

/**

  • National American Holidays

  • @param string $year

  • @return array
    */
    public static function getNationalAmericanHolidays($year) {

    // January 1 - New Year's Day (Observed) // Third Monday in January - Birthday of Martin Luther King, Jr. // Third Monday in February - Washington’s Birthday / President's Day // Last Monday in May - Memorial Day // July 4 - Independence Day // First Monday in September - Labor Day // Second Monday in October - Columbus Day // November 11 - Veterans’ Day (Observed) // Fourth Thursday in November Thanksgiving Day // December 25 - Christmas Day $bankHolidays = array( ['New Years Day'] => $year . "-01-01", ['Martin Luther King Jr Birthday'] => "". date("Y-m-d",strtotime("third Monday of January " . $year) ), ['Washingtons Birthday'] => "". date("Y-m-d",strtotime("third Monday of February " . $year) ), ['Memorial Day'] => "". date("Y-m-d",strtotime("last Monday of May " . $year) ), ['Independance Day'] => $year . "-07-04", ['Labor Day'] => "". date("Y-m-d",strtotime("first Monday of September " . $year) ), ['Columbus Day'] => "". date("Y-m-d",strtotime("second Monday of October " . $year) ), ['Veterans Day'] => $year . "-11-11", ['Thanksgiving Day'] => "". date("Y-m-d",strtotime("fourth Thursday of November " . $year) ), ['Christmas Day'] => $year . "-12-25" );

    return $bankHolidays;

}

Solution 33 - Php

I figured you might want to include a list of UK holidays, so here's that:

<?php

class ProductionDays
{

    private function getHolidays()
    {
        //from https://www.gov.uk/bank-holidays.json
        $holidaysData = '{"england-and-wales":{"division":"england-and-wales","events":[{"title":"New Year’s Day","date":"2015-01-01","notes":"","bunting":true},{"title":"Good Friday","date":"2015-04-03","notes":"","bunting":false},{"title":"Easter Monday","date":"2015-04-06","notes":"","bunting":true},{"title":"Early May bank holiday","date":"2015-05-04","notes":"","bunting":true},{"title":"Spring bank holiday","date":"2015-05-25","notes":"","bunting":true},{"title":"Summer bank holiday","date":"2015-08-31","notes":"","bunting":true},{"title":"Christmas Day","date":"2015-12-25","notes":"","bunting":true},{"title":"Boxing Day","date":"2015-12-28","notes":"Substitute day","bunting":true},{"title":"New Year’s Day","date":"2016-01-01","notes":"","bunting":true},{"title":"Good Friday","date":"2016-03-25","notes":"","bunting":false},{"title":"Easter Monday","date":"2016-03-28","notes":"","bunting":true},{"title":"Early May bank holiday","date":"2016-05-02","notes":"","bunting":true},{"title":"Spring bank holiday","date":"2016-05-30","notes":"","bunting":true},{"title":"Summer bank holiday","date":"2016-08-29","notes":"","bunting":true},{"title":"Boxing Day","date":"2016-12-26","notes":"","bunting":true},{"title":"Christmas Day","date":"2016-12-27","notes":"Substitute day","bunting":true},{"title":"New Year’s Day","date":"2017-01-02","notes":"Substitute day","bunting":true},{"title":"Good Friday","date":"2017-04-14","notes":"","bunting":false},{"title":"Easter Monday","date":"2017-04-17","notes":"","bunting":true},{"title":"Early May bank holiday","date":"2017-05-01","notes":"","bunting":true},{"title":"Spring bank holiday","date":"2017-05-29","notes":"","bunting":true},{"title":"Summer bank holiday","date":"2017-08-28","notes":"","bunting":true},{"title":"Christmas Day","date":"2017-12-25","notes":"","bunting":true},{"title":"Boxing Day","date":"2017-12-26","notes":"","bunting":true},{"title":"New Year’s Day","date":"2018-01-01","notes":"","bunting":true},{"title":"Good Friday","date":"2018-03-30","notes":"","bunting":false},{"title":"Easter Monday","date":"2018-04-02","notes":"","bunting":true},{"title":"Early May bank holiday","date":"2018-05-07","notes":"","bunting":true},{"title":"Spring bank holiday","date":"2018-05-28","notes":"","bunting":true},{"title":"Summer bank holiday","date":"2018-08-27","notes":"","bunting":true},{"title":"Christmas Day","date":"2018-12-25","notes":"","bunting":true},{"title":"Boxing Day","date":"2018-12-26","notes":"","bunting":true},{"title":"New Year’s Day","date":"2019-01-01","notes":"","bunting":true},{"title":"Good Friday","date":"2019-04-19","notes":"","bunting":false},{"title":"Easter Monday","date":"2019-04-22","notes":"","bunting":true},{"title":"Early May bank holiday","date":"2019-05-06","notes":"","bunting":true},{"title":"Spring bank holiday","date":"2019-05-27","notes":"","bunting":true},{"title":"Summer bank holiday","date":"2019-08-26","notes":"","bunting":true},{"title":"Christmas Day","date":"2019-12-25","notes":"","bunting":true},{"title":"Boxing Day","date":"2019-12-26","notes":"","bunting":true},{"title":"New Year’s Day","date":"2020-01-01","notes":"","bunting":true},{"title":"Good Friday","date":"2020-04-10","notes":"","bunting":false},{"title":"Easter Monday","date":"2020-04-13","notes":"","bunting":true},{"title":"Early May bank holiday (VE day)","date":"2020-05-08","notes":"","bunting":true},{"title":"Spring bank holiday","date":"2020-05-25","notes":"","bunting":true},{"title":"Summer bank holiday","date":"2020-08-31","notes":"","bunting":true},{"title":"Christmas Day","date":"2020-12-25","notes":"","bunting":true},{"title":"Boxing Day","date":"2020-12-28","notes":"Substitute day","bunting":true},{"title":"New Year’s Day","date":"2021-01-01","notes":"","bunting":true},{"title":"Good Friday","date":"2021-04-02","notes":"","bunting":false},{"title":"Easter Monday","date":"2021-04-05","notes":"","bunting":true},{"title":"Early May bank holiday","date":"2021-05-03","notes":"","bunting":true},{"title":"Spring bank holiday","date":"2021-05-31","notes":"","bunting":true},{"title":"Summer bank holiday","date":"2021-08-30","notes":"","bunting":true},{"title":"Christmas Day","date":"2021-12-27","notes":"Substitute day","bunting":true},{"title":"Boxing Day","date":"2021-12-28","notes":"Substitute day","bunting":true}]},"scotland":{"division":"scotland","events":[{"title":"New Year’s Day","date":"2015-01-01","notes":"","bunting":true},{"title":"2nd January","date":"2015-01-02","notes":"","bunting":true},{"title":"Good Friday","date":"2015-04-03","notes":"","bunting":false},{"title":"Early May bank holiday","date":"2015-05-04","notes":"","bunting":true},{"title":"Spring bank holiday","date":"2015-05-25","notes":"","bunting":true},{"title":"Summer bank holiday","date":"2015-08-03","notes":"","bunting":true},{"title":"St Andrew’s Day","date":"2015-11-30","notes":"","bunting":true},{"title":"Christmas Day","date":"2015-12-25","notes":"","bunting":true},{"title":"Boxing Day","date":"2015-12-28","notes":"Substitute day","bunting":true},{"title":"New Year’s Day","date":"2016-01-01","notes":"","bunting":true},{"title":"2nd January","date":"2016-01-04","notes":"Substitute day","bunting":true},{"title":"Good Friday","date":"2016-03-25","notes":"","bunting":false},{"title":"Early May bank holiday","date":"2016-05-02","notes":"","bunting":true},{"title":"Spring bank holiday","date":"2016-05-30","notes":"","bunting":true},{"title":"Summer bank holiday","date":"2016-08-01","notes":"","bunting":true},{"title":"St Andrew’s Day","date":"2016-11-30","notes":"","bunting":true},{"title":"Boxing Day","date":"2016-12-26","notes":"","bunting":true},{"title":"Christmas Day","date":"2016-12-27","notes":"Substitute day","bunting":true},{"title":"2nd January","date":"2017-01-02","notes":"","bunting":true},{"title":"New Year’s Day","date":"2017-01-03","notes":"Substitute day","bunting":true},{"title":"Good Friday","date":"2017-04-14","notes":"","bunting":false},{"title":"Early May bank holiday","date":"2017-05-01","notes":"","bunting":true},{"title":"Spring bank holiday","date":"2017-05-29","notes":"","bunting":true},{"title":"Summer bank holiday","date":"2017-08-07","notes":"","bunting":true},{"title":"St Andrew’s Day","date":"2017-11-30","notes":"","bunting":true},{"title":"Christmas Day","date":"2017-12-25","notes":"","bunting":true},{"title":"Boxing Day","date":"2017-12-26","notes":"","bunting":true},{"title":"New Year’s Day","date":"2018-01-01","notes":"","bunting":true},{"title":"2nd January","date":"2018-01-02","notes":"","bunting":true},{"title":"Good Friday","date":"2018-03-30","notes":"","bunting":false},{"title":"Early May bank holiday","date":"2018-05-07","notes":"","bunting":true},{"title":"Spring bank holiday","date":"2018-05-28","notes":"","bunting":true},{"title":"Summer bank holiday","date":"2018-08-06","notes":"","bunting":true},{"title":"St Andrew’s Day","date":"2018-11-30","notes":"","bunting":true},{"title":"Christmas Day","date":"2018-12-25","notes":"","bunting":true},{"title":"Boxing Day","date":"2018-12-26","notes":"","bunting":true},{"title":"New Year’s Day","date":"2019-01-01","notes":"","bunting":true},{"title":"2nd January","date":"2019-01-02","notes":"","bunting":true},{"title":"Good Friday","date":"2019-04-19","notes":"","bunting":false},{"title":"Early May bank holiday","date":"2019-05-06","notes":"","bunting":true},{"title":"Spring bank holiday","date":"2019-05-27","notes":"","bunting":true},{"title":"Summer bank holiday","date":"2019-08-05","notes":"","bunting":true},{"title":"St Andrew’s Day","date":"2019-12-02","notes":"Substitute day","bunting":true},{"title":"Christmas Day","date":"2019-12-25","notes":"","bunting":true},{"title":"Boxing Day","date":"2019-12-26","notes":"","bunting":true},{"title":"New Year’s Day","date":"2020-01-01","notes":"","bunting":true},{"title":"2nd January","date":"2020-01-02","notes":"","bunting":true},{"title":"Good Friday","date":"2020-04-10","notes":"","bunting":false},{"title":"Early May bank holiday (VE day)","date":"2020-05-08","notes":"","bunting":true},{"title":"Spring bank holiday","date":"2020-05-25","notes":"","bunting":true},{"title":"Summer bank holiday","date":"2020-08-03","notes":"","bunting":true},{"title":"St Andrew’s Day","date":"2020-11-30","notes":"","bunting":true},{"title":"Christmas Day","date":"2020-12-25","notes":"","bunting":true},{"title":"Boxing Day","date":"2020-12-28","notes":"Substitute day","bunting":true},{"title":"New Year’s Day","date":"2021-01-01","notes":"","bunting":true},{"title":"2nd January","date":"2021-01-04","notes":"Substitute day","bunting":true},{"title":"Good Friday","date":"2021-04-02","notes":"","bunting":false},{"title":"Early May bank holiday","date":"2021-05-03","notes":"","bunting":true},{"title":"Spring bank holiday","date":"2021-05-31","notes":"","bunting":true},{"title":"Summer bank holiday","date":"2021-08-02","notes":"","bunting":true},{"title":"St Andrew’s Day","date":"2021-11-30","notes":"","bunting":true},{"title":"Christmas Day","date":"2021-12-27","notes":"Substitute day","bunting":true},{"title":"Boxing Day","date":"2021-12-28","notes":"Substitute day","bunting":true}]},"northern-ireland":{"division":"northern-ireland","events":[{"title":"New Year’s Day","date":"2015-01-01","notes":"","bunting":true},{"title":"St Patrick’s Day","date":"2015-03-17","notes":"","bunting":true},{"title":"Good Friday","date":"2015-04-03","notes":"","bunting":false},{"title":"Easter Monday","date":"2015-04-06","notes":"","bunting":true},{"title":"Early May bank holiday","date":"2015-05-04","notes":"","bunting":true},{"title":"Spring bank holiday","date":"2015-05-25","notes":"","bunting":true},{"title":"Battle of the Boyne (Orangemen’s Day)","date":"2015-07-13","notes":"Substitute day","bunting":false},{"title":"Summer bank holiday","date":"2015-08-31","notes":"","bunting":true},{"title":"Christmas Day","date":"2015-12-25","notes":"","bunting":true},{"title":"Boxing Day","date":"2015-12-28","notes":"Substitute day","bunting":true},{"title":"New Year’s Day","date":"2016-01-01","notes":"","bunting":true},{"title":"St Patrick’s Day","date":"2016-03-17","notes":"","bunting":true},{"title":"Good Friday","date":"2016-03-25","notes":"","bunting":false},{"title":"Easter Monday","date":"2016-03-28","notes":"","bunting":true},{"title":"Early May bank holiday","date":"2016-05-02","notes":"","bunting":true},{"title":"Spring bank holiday","date":"2016-05-30","notes":"","bunting":true},{"title":"Battle of the Boyne (Orangemen’s Day)","date":"2016-07-12","notes":"","bunting":false},{"title":"Summer bank holiday","date":"2016-08-29","notes":"","bunting":true},{"title":"Boxing Day","date":"2016-12-26","notes":"","bunting":true},{"title":"Christmas Day","date":"2016-12-27","notes":"Substitute day","bunting":true},{"title":"New Year’s Day","date":"2017-01-02","notes":"Substitute day","bunting":true},{"title":"St Patrick’s Day","date":"2017-03-17","notes":"","bunting":true},{"title":"Good Friday","date":"2017-04-14","notes":"","bunting":false},{"title":"Easter Monday","date":"2017-04-17","notes":"","bunting":true},{"title":"Early May bank holiday","date":"2017-05-01","notes":"","bunting":true},{"title":"Spring bank holiday","date":"2017-05-29","notes":"","bunting":true},{"title":"Battle of the Boyne (Orangemen’s Day)","date":"2017-07-12","notes":"","bunting":false},{"title":"Summer bank holiday","date":"2017-08-28","notes":"","bunting":true},{"title":"Christmas Day","date":"2017-12-25","notes":"","bunting":true},{"title":"Boxing Day","date":"2017-12-26","notes":"","bunting":true},{"title":"New Year’s Day","date":"2018-01-01","notes":"","bunting":true},{"title":"St Patrick’s Day","date":"2018-03-19","notes":"Substitute day","bunting":true},{"title":"Good Friday","date":"2018-03-30","notes":"","bunting":false},{"title":"Easter Monday","date":"2018-04-02","notes":"","bunting":true},{"title":"Early May bank holiday","date":"2018-05-07","notes":"","bunting":true},{"title":"Spring bank holiday","date":"2018-05-28","notes":"","bunting":true},{"title":"Battle of the Boyne (Orangemen’s Day)","date":"2018-07-12","notes":"","bunting":false},{"title":"Summer bank holiday","date":"2018-08-27","notes":"","bunting":true},{"title":"Christmas Day","date":"2018-12-25","notes":"","bunting":true},{"title":"Boxing Day","date":"2018-12-26","notes":"","bunting":true},{"title":"New Year’s Day","date":"2019-01-01","notes":"","bunting":true},{"title":"St Patrick’s Day","date":"2019-03-18","notes":"Substitute day","bunting":true},{"title":"Good Friday","date":"2019-04-19","notes":"","bunting":false},{"title":"Easter Monday","date":"2019-04-22","notes":"","bunting":true},{"title":"Early May bank holiday","date":"2019-05-06","notes":"","bunting":true},{"title":"Spring bank holiday","date":"2019-05-27","notes":"","bunting":true},{"title":"Battle of the Boyne (Orangemen’s Day)","date":"2019-07-12","notes":"","bunting":false},{"title":"Summer bank holiday","date":"2019-08-26","notes":"","bunting":true},{"title":"Christmas Day","date":"2019-12-25","notes":"","bunting":true},{"title":"Boxing Day","date":"2019-12-26","notes":"","bunting":true},{"title":"New Year’s Day","date":"2020-01-01","notes":"","bunting":true},{"title":"St Patrick’s Day","date":"2020-03-17","notes":"","bunting":true},{"title":"Good Friday","date":"2020-04-10","notes":"","bunting":false},{"title":"Easter Monday","date":"2020-04-13","notes":"","bunting":true},{"title":"Early May bank holiday (VE day)","date":"2020-05-08","notes":"","bunting":true},{"title":"Spring bank holiday","date":"2020-05-25","notes":"","bunting":true},{"title":"Battle of the Boyne (Orangemen’s Day)","date":"2020-07-13","notes":"Substitute day","bunting":false},{"title":"Summer bank holiday","date":"2020-08-31","notes":"","bunting":true},{"title":"Christmas Day","date":"2020-12-25","notes":"","bunting":true},{"title":"Boxing Day","date":"2020-12-28","notes":"Substitute day","bunting":true},{"title":"New Year’s Day","date":"2021-01-01","notes":"","bunting":true},{"title":"St Patrick’s Day","date":"2021-03-17","notes":"","bunting":true},{"title":"Good Friday","date":"2021-04-02","notes":"","bunting":false},{"title":"Easter Monday","date":"2021-04-05","notes":"","bunting":true},{"title":"Early May bank holiday","date":"2021-05-03","notes":"","bunting":true},{"title":"Spring bank holiday","date":"2021-05-31","notes":"","bunting":true},{"title":"Battle of the Boyne (Orangemen’s Day)","date":"2021-07-12","notes":"","bunting":false},{"title":"Summer bank holiday","date":"2021-08-30","notes":"","bunting":true},{"title":"Christmas Day","date":"2021-12-27","notes":"Substitute day","bunting":true},{"title":"Boxing Day","date":"2021-12-28","notes":"Substitute day","bunting":true}]}}';

        $holidaysArray = json_decode($holidaysData, true);
        $holidays = [];
        foreach ($holidaysArray["england-and-wales"]['events'] as $event) {
            $holidays[] = $event['date'];
        }
        return $holidays;
    }

    public function getWorkingDays($dateFrom, $dateTo)
    {
        $holidays = $this->getHolidays();
        $startDate = new DateTime($dateFrom);
        $endDate = new DateTime($dateTo);
        $interval = new DateInterval('P1D');
        $dateRange = new DatePeriod($startDate, $interval, $endDate);
        $results = [];
        foreach ($dateRange as $date) {
            if ($date->format('N') < 6 && !in_array($date->format('Y-m-d'), $holidays)) {
                $results[] = $date->format("Y-m-d");
            }
        }
        return $results;
    }

}

$dateFrom = '01-01-2020';
$dateTo = '31-01-2020';

$productionDays = new ProductionDays();
$getWorkingDays = $productionDays->getWorkingDays($dateFrom, $dateTo);

echo json_encode($getWorkingDays);

Solution 34 - Php

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.

Solution 35 - Php

There is a Formula:

number_of_days - math_round_down(10 * (number_of_days / (business_days_in_a_week * days_in_a_week)))

Tada! you calculate the numbers of business day, in a month, in a week, in a whatever you want.

math_round_down () is a hypothetical method, which implements a mathematical function that rounds down.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAdamTheHuttView Question on Stackoverflow
Solution 1 - PhpflamingLogosView Answer on Stackoverflow
Solution 2 - PhpGlavićView Answer on Stackoverflow
Solution 3 - PhpTimView Answer on Stackoverflow
Solution 4 - PhpJames PastaView Answer on Stackoverflow
Solution 5 - PhpSuresh KamrushiView Answer on Stackoverflow
Solution 6 - PhpBobbinView Answer on Stackoverflow
Solution 7 - PhpGeorge JohnView Answer on Stackoverflow
Solution 8 - PhpCraig FrancisView Answer on Stackoverflow
Solution 9 - PhpAlexView Answer on Stackoverflow
Solution 10 - PhpatisView Answer on Stackoverflow
Solution 11 - PhpeasycodingclubView Answer on Stackoverflow
Solution 12 - PhpMahmoud ObaidView Answer on Stackoverflow
Solution 13 - PhpLairdView Answer on Stackoverflow
Solution 14 - PhpPaige RutenView Answer on Stackoverflow
Solution 15 - PhpmcgrailmView Answer on Stackoverflow
Solution 16 - PhpVijayView Answer on Stackoverflow
Solution 17 - PhppawproView Answer on Stackoverflow
Solution 18 - PhpmarveView Answer on Stackoverflow
Solution 19 - PhpshepardtoneView Answer on Stackoverflow
Solution 20 - PhpJamesView Answer on Stackoverflow
Solution 21 - PhpIan CarterView Answer on Stackoverflow
Solution 22 - PhpEnricoView Answer on Stackoverflow
Solution 23 - PhpNicolas GiszpencView Answer on Stackoverflow
Solution 24 - PhpRichard VarnoView Answer on Stackoverflow
Solution 25 - PhpPratik ThakkarView Answer on Stackoverflow
Solution 26 - PhpDustin WView Answer on Stackoverflow
Solution 27 - PhpDevlshOneView Answer on Stackoverflow
Solution 28 - PhpBig JoeView Answer on Stackoverflow
Solution 29 - PhpMax LuzhkovView Answer on Stackoverflow
Solution 30 - PhpAbkarinoView Answer on Stackoverflow
Solution 31 - PhpjurchiksView Answer on Stackoverflow
Solution 32 - PhpTonyView Answer on Stackoverflow
Solution 33 - PhpjpswadeView Answer on Stackoverflow
Solution 34 - PhpFAOMView Answer on Stackoverflow
Solution 35 - PhpRodrigo ReisView Answer on Stackoverflow