我需要计算大量日期之间的工作日。因此,我正在寻找能够快速完成任务的功能
设置所有假期:
public function getArrayOfHolidays($year, $holidays= array(), $format = 'Y-m-d')
{
$special1= new DateTime('2017-10-31');
array_push($holidays, $special1->format($format));
$newyear= new DateTime();
$newyear->modify('1 january ' . $year);
array_push($holidays, $newyear->format($format));
$holyThreeKings= new DateTime();
$holyThreeKings->modify('6 january ' . $year);
array_push($holidays, $holyThreeKings->format($format));
$layborDay= new DateTime();
$layborDay->modify('1 may ' . $year);
array_push($holidays, $layborDay->format($format));
$unityDay= new DateTime();
$unityDay->modify('3 october ' . $year);
array_push($holidays, $unityDay->format($format));
$allSaintsDay= new DateTime();
$allSaintsDay->modify('1 november ' . $year);
array_push($holidays, $allSaintsDay->format($format));
$christmasEve= new DateTime();
$christmasEve->modify('24 december ' . $year);
array_push($holidays, $christmasEve->format($format));
$christmas1 = new DateTime();
$christmas1->modify('25 december ' . $year);
array_push($holidays, $christmas1->format($format));
$christmas2 = new DateTime();
$christmas2->modify('26 december ' . $year);
array_push($holidays, $christmas2->format($format));
$silvester = new DateTime();
$silvester->modify('31 december ' . $year);
array_push($holidays, $silvester->format($format));
$easterSunday= new DateTime(date('Y-m-d', easter_date($year)));
$easterSunday->modify('+1 day');
array_push($holidays, $easterSunday->format($format));
$goodFriday = new DateTime($ostersonntag->format('Y-m-d'));
$goodFriday->modify('last friday');
array_push($holidays, $goodFriday->format($format));
$easterMonday= new DateTime($ostersonntag->format('Y-m-d'));
$easterMonday->modify('next monday');
array_push($holidays, $easterMonday->format($format));
$ascensionOfJesus= new DateTime($ostersonntag->format('Y-m-d'));
$ascensionOfJesus->modify('+39 days');
array_push($holidays, $ascensionOfJesus->format($format));
$pentecostSunday= new DateTime($ostersonntag->format('Y-m-d'));
$pentecostSunday->modify('+49 days');
array_push($holidays, $pentecostSunday->format($format));
$pentecostMonday= new DateTime($ostersonntag->format('Y-m-d'));
$pentecostMonday->modify('+50 days');
array_push($holidays, $pentecostMonday->format($format));
$corpusChristi = new DateTime($pentecostMonday->format('Y-m-d'));
$corpusChristi ->modify('next sunday');
$corpusChristi ->modify('next thursday');
array_push($holidays, $corpusChristi ->format($format));
return $holidays;
}
这最多可以运行3次,以确保如果有年份间计算的日期,那么前一年和下一年都会有所有的神圣日子。因此不应该有那么高的影响力。我还想展示它,因为肯定有更好的方法吗?我可以预测接下来的X年并将它们存储在数据库中,也许会更好。
现在这是我用来计算工作日的函数:
public 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;
}
现在该函数在循环中被调用大约40.000次,大约需要20秒。有更有效的方法吗?或者是缓存它的唯一选择?
答案 0 :(得分:0)
我建议您使用florianv / business composer库来执行此操作:
https://github.com/florianv/business
文档很好,运行速度比我自己想出的任何东西都快。
一旦您定义了参数(每天,假期的营业时间),您就可以使用&#34;时间线&#34;用于确定企业的两个日期之间的天数。