在php

时间:2017-12-05 17:23:44

标签: php

我需要计算大量日期之间的工作日。因此,我正在寻找能够快速完成任务的功能

设置所有假期:

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秒。有更有效的方法吗?或者是缓存它的唯一选择?

1 个答案:

答案 0 :(得分:0)

我建议您使用florianv / business composer库来执行此操作:

https://github.com/florianv/business

文档很好,运行速度比我自己想出的任何东西都快。

一旦您定义了参数(每天,假期的营业时间),您就可以使用&#34;时间线&#34;用于确定企业的两个日期之间的天数。