我需要一种在 PHP 中添加“工作日”的方法。例如,星期五 12/5 + 3 个工作日 = 星期三 12/10。
至少我需要代码来理解周末,但理想情况下它也应该考虑到美国联邦假期。我敢肯定,如有必要,我可以通过蛮力提出解决方案,但我希望那里有更优雅的方法。任何人?
谢谢。
这是PHP 手册中 date() 函数页面上用户评论中的一个函数。这是对评论中早期功能的改进,增加了对闰年的支持。
输入开始日期和结束日期,以及可能介于两者之间的任何假期的数组,它以整数形式返回工作日:
<?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('Y-m-d'), $holidayDays)) continue;
if (in_array($period->format('*-m-d'), $holidayDays)) continue;
$days++;
}
return $days;
}
date()函数有一些参数应该会有所帮助。如果你检查 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("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;
}
$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);
这是一个将营业日添加到日期的功能
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');
另一篇文章提到了 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('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);
带上假期...
你可以试试这个更简单的功能。
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;
}
}
我的版本基于@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('Y-m-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('Y-m-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 day', $timestamp) 慢了一点,但它更可靠。
从给定日期增加或减少工作日的功能,这不考虑假期。
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));
检测工作时间的蛮力尝试 - 周一至周五上午 8 点至下午 4 点:
if (date('N')<6 && date('G')>8 && date('G')<16) {
// we have a working time (or check for holidays)
}
以下是从给定日期计算工作日的工作代码。
<?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"
这个代码片段很容易计算没有周末和节假日的工作日:
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)
这是每天没有 for 循环的另一种解决方案。
$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
对于假期,以 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);
我有同样的需求,我从 bobbin 的第一个例子开始,最后得到了这个
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);
}
某人
<?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>";
?>
变体 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;
}
通过执行以下操作,可以使用上述的变体来考虑额外的假期。笔记!确保所有时间戳都是一天中的同一时间(即午夜)。
制作一组假期日期(作为unixtimestamps),即:
$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('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;
}
<?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('Y-m-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('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";
计算两个日期之间的工作日,包括假期和自定义工作周
答案并不是那么微不足道 - 因此我的建议是使用一个类,您可以在其中配置更多内容,而不是依赖简单的功能(或假设固定的语言环境和文化)。要在一定数量的工作日后获取日期,您将:
功能方法
/**
* @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;
这是另一种解决方案,它比使用 in_array 检查假期快近 25%:
/**
* 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));
}
我知道我迟到了,但我使用 Marcos J. Montes 的这套旧函数来计算假期和工作日。他花时间为复活节添加了 1876 年的算法,并添加了美国所有主要的假期。这可以很容易地为其他国家/地区更新。
//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;
}
函数 get_business_days_forward_from_date($num_days, $start_date='', $rtn_fmt='Ymd') {
// $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");
}
我只是让我的函数基于 Bobbin 和 mcgralm 代码工作,添加一些对我来说完美的东西。
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.
add_business_days 有一个小错误。使用现有函数尝试以下操作,输出将是星期六。
开始日期 = 要添加的星期五工作日 = 1 个假期数组 = 添加下一个星期一的日期。
我已经在下面的函数中解决了这个问题。
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);}
对上面 James Pasta 提供的功能的增强,包括所有联邦假期,并更正了 7 月 4 日(上面计算为 6 月 4 日!),还包括假期名称作为数组键......
/**
* 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;
}
感谢 Bobbin、mcgrailm、Tony、James Pasta 和其他一些在这里发帖的人。我编写了自己的函数来将工作日添加到日期,但使用我在这里找到的一些代码对其进行了修改。这将处理周末/节假日的开始日期。这也将处理营业时间。我添加了一些注释并分解了代码以使其更易于阅读。
<?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);
?>
就个人而言,我认为这是一个更干净、更简洁的解决方案:
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;
}
只需将建议的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");
PHPClasses 有一个很好的类,名为PHP Working Days。你可以看看这个类。
如果您需要获取两个日期之间的时间,您可以使用https://github.com/maximnara/business-days-counter。它很简单,但现在只适用于 laravel$diffInSeconds = $this->datesCounter->getDifferenceInSeconds(Carbon::create(2019, 1, 1), Carbon::now(), DateCounter::COUNTRY_FR);
它不计算公共假期和周末,您可以设置工作间隔,例如从 9 到 18 与启动时间或没有。
或者,如果您只需要周末并且使用 Carbon,您可以使用内置功能:
$date1 = Carbon::create(2019, 1, 1)->endOfDay();
$date2 = $dt->copy()->startOfDay();
$diff = $date1->diffFiltered(CarbonInterval::minute(), function(Carbon $date) {
return !$date->isWeekend();
}, $date2, true);
但它会每隔一分钟间隔,对于位间隔可能需要一段时间。
我想你可能想要包括英国假期的列表,所以这里是:
<?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);
尽管在撰写本文时这个问题有超过 35 个答案,但是对于这个问题有更好的解决方案。
@flamingLogos提供了一个基于数学且不包含任何循环的答案。该解决方案的时间复杂度为Θ(1)
. 然而,它背后的数学非常复杂,尤其是闰年的处理。
@Glavić提供了一个简洁而优雅的好解决方案。但它不会在恒定时间内执行计算,因此它可能会产生拒绝服务 (DOS) 攻击,或者如果在 10 年或 100 年这样的大周期内使用,它可能会产生拒绝服务 (DOS) 攻击,或者至少会超时,因为它以 1 天的间隔循环。
所以我提出了一种数学方法,它具有恒定的时间但非常易读。
这个想法是计算有多少天有完整的星期。
<?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];
}
该函数的输入格式为Y-m-d
php 格式或yyyy-mm-dd
一般日期格式。
get holiday 函数将返回从开始年份到结束年份的假期日期数组。
有一个公式:
number_of_days - math_round_down(10 * (number_of_days / (business_days_in_a_week * days_in_a_week)))
多田!你计算工作日数,一个月,一周,任何你想要的。
math_round_down() 是一个假设的方法,它实现了一个向下舍入的数学函数。
https://github.com/Arbitr108/useful_php这是一个可以将工作日估计为日历的类。因此,估计后只需将日历期间添加到您需要的日期