I know I'm late for the party, but I use this old set of Marcos Montes functions to define weekends and workdays. He took the time to add an algorithm from 1876 for Easter, and he added all the major American holidays. It can be easily 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 & 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 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 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("Ymd", $working_date); if (!isWeekend($working_date) && !confirm_holiday(date("Ymd", 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("Ymd"); } $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; }