Расчет рабочих дней

Мне нужен метод добавления «рабочих дней» в PHP. Например, пятница 12/5 + 3 рабочих дня = среда 12/10.

Как минимум, мне нужен код для понимания выходных, но в идеале он должен учитывать и федеральные праздники США. Я уверен, что при необходимости придумаю решение грубой силой, но я надеюсь, что там будет более элегантный подход. Кто угодно?

Благодарю.

Solutions Collecting From Web of "Расчет рабочих дней"

Вот функция из комментариев пользователя на странице функции date () в руководстве по PHP. Это улучшение более ранней функции в комментариях, которая добавляет поддержку високосных лет.

Введите даты начала и окончания, а также массив любых праздников, которые могут находиться между ними, и возвращает рабочие дни в виде целого числа:

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

Получите количество рабочих дней без отпуска между двумя датами:

Пример использования:

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

Вывод:

 3 

Функция:

 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('Ym-d'), $holidayDays)) continue; if (in_array($period->format('*-m-d'), $holidayDays)) continue; $days++; } return $days; } 

Есть некоторые аргументы для функции date (), которые должны помочь. Если вы проверите дату («w»), он даст вам номер для дня недели, от 0 до воскресенья до 6 в субботу. Так .. может быть что-то вроде ..

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

Это всего лишь примерный пример.

Расчет поселений не является стандартным в каждом государстве. Я пишу банковское приложение, для которого мне нужны какие-то жесткие бизнес-правила, но все же может получить только приблизительный стандарт.

 /** * 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("Ymd",strtotime("last Monday of May " . $year) ) // Memorial Day , $year . "-07-04" // Independence Day (corrected) , "". date("Ymd",strtotime("first Monday of September " . $year) ) // Labor Day , $year . "-11-11" // Veterans Day , "". date("Ymd",strtotime("fourth Thursday of November " . $year) ) // Thanksgiving , $year . "-12-25" // XMAS ); return $bankHolidays; } 

Вот функция добавления дней работы к дате

  function add_business_days($startdate,$buisnessdays,$holidays,$dateformat){ $i=1; $dayx = strtotime($startdate); while($i < $buisnessdays){ $day = date('N',$dayx); $date = date('Ym-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'); 

В другом сообщении упоминается getWorkingDays (из комментариев php.net и включено сюда), но я думаю, что он ломается, если вы начинаете в воскресенье и заканчиваете рабочий день.

Используя следующее (вам нужно включить функцию getWorkingDays из предыдущего сообщения)

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

Дает результат как 5 не 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 

Для генерации вышеизложенного использовалась следующая функция.

  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('Ym-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); 

Приведи праздничные дни …

 $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("Ymd"),$holiday)) $result[] = $date->format("Ymd"); } echo "<pre>";print_r($result); 

Функция добавления или вычитания рабочих дней с определенной даты не учитывает праздников.

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

используйте так …

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

Моя версия, основанная на работе @mcgrailm … изменилась, потому что отчет нужно было пересмотреть в течение 3 рабочих дней, и если он был отправлен в выходные, подсчет начнется в следующий понедельник:

 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('Ym-d', $current_date), $holidays)) { $business_days--; } if ($business_days > 0) { $current_date = strtotime('+1 day', $current_date); } } return $current_date; } 

И выработка разницы двух дат в рабочие дни:

 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('Ym-d', $current_date), $holidays)) { $business_days++; } if ($current_date <= $end_date) { $current_date = strtotime('+1 day', $current_date); } } return $business_days; } 

В качестве примечания, каждый, кто использует 86400, или 24 * 60 * 60, пожалуйста, не … ваше время забывания меняется с зимнего / летнего времени, где в день это не ровно 24 часа. Хотя это немного медленнее strtotime ('+ 1 день', $ timestamp), он намного надежнее.

Жесткая попытка обнаружить рабочее время – с понедельника по пятницу с 8:00 до 16:00:

 if (date('N')<6 && date('G')>8 && date('G')<16) { // we have a working time (or check for holidays) } 

Для этого я создал приличную библиотеку.

https://github.com/andrejsstepanovs/business-days-calculator

Он стабилен и готов к производству.

Вы можете попробовать эту функцию, которая проще.

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

Для праздников создайте массив дней в каком-то формате, который может дать date (). Пример:

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

Затем используйте функции in_array () и date (), чтобы проверить, является ли временная метка праздником:

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

У меня была такая же потребность, я начал с первого примера бобины и в итоге

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

кто-то

Вариант 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; } 

Вариант 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; } 

Вариант 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; } 

Дополнительные соображения об отдыхе могут быть сделаны с использованием вариантов вышеизложенного, выполнив следующее. Заметка! убедитесь, что все отметки времени – это одно и то же время суток (т.е. полночь).

Сделайте множество дат отпуска (как unimxtimestamps), то есть:

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

Изменить строку:

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

быть :

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

Готово!

 <?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('Ym-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; } 
 <?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); } ?> 

Рекурсивное решение. Его можно легко изменить, чтобы отслеживать и возвращать последнюю дату.

 // 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('Ym-d'); return getWorkingDays($currentDate, $numBusDays, $holidays, $resultDates); } // test $days = getWorkingDays('2008-12-05', 4); print_r($days); 
 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('Ym-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('Ym-d',$result)) && (!(is_holiday(date('Ym-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"; 
 <?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>"; ?> 

Ниже приведен рабочий код для расчета рабочих рабочих дней с определенной даты.

 <?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("Ymd", $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" 

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

 // $start_date will default to today if ($start_date=='') { $start_date = date("Ymd"); } $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('Ym-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('Ym-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"); 

}

У add_business_days есть небольшая ошибка. Попробуйте следующее с существующей функцией, и выход будет в субботу.

Startdate = Friday Business days to add = 1 Holidays array = Добавить дату для следующего понедельника.

Я исправил это в своей функции ниже.

 function add_business_days($startdate, $buisnessdays, $holidays = array(), $dateformat = 'Ym-d'){ $i= 1; $dayx= strtotime($startdate); $buisnessdays= ceil($buisnessdays); while($i < $buisnessdays) { $day= date('N',$dayx); $date= date('Ym-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('Ym-d',$dayx); while($day >= 6 || in_array($date,$holidays)) { $dayx= strtotime($date.' +1 day'); $day= date('N',$dayx); $date= date('Ym-d',$dayx); } return date($dateformat, $dayx);} 

Я просто использую свою функцию, основанную на коде Bobbin и mcgrailm, добавляя некоторые вещи, которые идеально подходят для меня.

 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('Ym-d',$enddate).' +1 day'); $day = date('N',$enddate); if($day < 6 && !in_array(date('Ym-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. 

Улучшение функции, предложенной Джеймсом Паста выше, включить все федеральные праздники и исправить 4 июля (было рассчитано как 4 июня выше!), А также включить название праздника в качестве ключа массива …

/ **
* Национальные американские праздники
* @param string $ year
* @return array
* /
публичная статическая функция 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("Ymd",strtotime("third Monday of January " . $year) ), ['Washingtons Birthday'] => "". date("Ymd",strtotime("third Monday of February " . $year) ), ['Memorial Day'] => "". date("Ymd",strtotime("last Monday of May " . $year) ), ['Independance Day'] => $year . "-07-04", ['Labor Day'] => "". date("Ymd",strtotime("first Monday of September " . $year) ), ['Columbus Day'] => "". date("Ymd",strtotime("second Monday of October " . $year) ), ['Veterans Day'] => $year . "-11-11", ['Thanksgiving Day'] => "". date("Ymd",strtotime("fourth Thursday of November " . $year) ), ['Christmas Day'] => $year . "-12-25" ); return $bankHolidays; 

}

Просто закончил писать API, который можно использовать для управления рабочими днями (ни один из этих решений не сработал для моей ситуации :-); ссылаясь на него здесь, если кто-то считает это полезным.

~ Нейт

Класс PHP для расчета рабочих дней

Спасибо Боббин, mcgrailm, Тони, Джеймс Паста и некоторые другие, которые разместили здесь. Я написал свою собственную функцию, чтобы добавить рабочие дни к дате, но изменил ее с помощью некоторого кода, который я нашел здесь. Это будет означать, что дата начала будет в выходные / праздничные дни. Это также будет обрабатывать часы работы. Я добавил несколько комментариев и разбил код, чтобы его было легче читать.

 <?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('Ym-d', $date), $holidays)){ $date = strtotime(date('Ym-d',$date).' +1 day'); } //Now that we know we have a business day, add 1 day to it $date = strtotime(date('Ym-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('Ym-d', $date), $holidays)){ $date = strtotime(date('Ym-d',$date).' +1 day'); } } return date('Ym-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("Ymd",strtotime("third Monday of January " . $year) ), 'Washingtons Birthday' => "". date("Ymd",strtotime("third Monday of February " . $year) ), 'Memorial Day' => "". date("Ymd",strtotime("last Monday of May " . $year) ), 'Independance Day' => $year . "-07-04", 'Labor Day' => "". date("Ymd",strtotime("first Monday of September " . $year) ), 'Columbus Day' => "". date("Ymd",strtotime("second Monday of October " . $year) ), 'Veterans Day' => $year . "-11-11", 'Thanksgiving Day' => "". date("Ymd",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("Ymd", strtotime("+ 1 day")); //Tomorrow } else { $start_date = date("Ymd"); //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); ?> 

Лично я считаю, что это более чистое и более сжатое решение:

 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("Ymd"), $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; } 

Просто отправьте предложенную new дату для этой функции.

Я просто создал эту функцию, которая, кажется, работает очень хорошо:

 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"); с 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"); 

В PHPClasses есть хороший класс для названных рабочих дней PHP . Вы можете проверить этот класс.

рассчитать рабочие дни между двумя датами, включая праздники и пользовательскую рабочую неделю

Ответ не такой тривиальный, поэтому моим предложением было бы использовать класс, в котором вы можете настроить больше, чем полагаться на упрощенную функцию (или предполагать фиксированный язык и культуру). Чтобы получить дату после определенного количества рабочих дней, вы:

  1. необходимо указать, какие рабочие дни вы будете работать (по умолчанию MON-FRI) – класс позволяет включать или отключать каждый будний день отдельно.
  2. необходимо знать, что вам необходимо учитывать государственные праздники (страну и государство)

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('Ym-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, 'DF 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, 'DF d Y', null, [0,1,1,1,1,1,1], ['01-01']); echo '<br>ten workingdays (MON-SAT) disregarding holidays: ',$ten_days; 

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 Ymd. * @param string $endDate -- End date of the range (included), formatted as Ymd. * @param array(string) $holidayDates -- OPTIONAL. Array of holidays dates, formatted as Ymd. (eg 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("Ymd");} } return count(array_diff($workingDays, $holidayDates)); }