Calculate business days

后端 未结 30 1823
猫巷女王i
猫巷女王i 2020-11-22 05:16

I need a method for adding \"business days\" in PHP. For example, Friday 12/5 + 3 business days = Wednesday 12/10.

At a minimum I need the code to understand weekend

相关标签:
30条回答
  • 2020-11-22 06:13

    For holidays, make an array of days in some format that date() can produce. Example:

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

    Then use the in_array() and date() functions to check if the timestamp represents a holiday:

    $day_of_year = date('M j', $timestamp);
    $is_holiday = in_array($day_of_year, $holidays);
    
    0 讨论(0)
  • 2020-11-22 06:13

    Here is a recursive solution. It can easily be modified to only keep track of and return the latest date.

    //  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);
    
    0 讨论(0)
  • 2020-11-22 06:14

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

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

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

    The additional holiday considerations can be made using variations of the above by doing the following. Note! assure all the timestamps are the same time of the day (i.e. midnight).

    Make an array of holiday dates (as unixtimestamps) i.e.:

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

    Modify line :

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

    to be :

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

    Done!

    <?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;
    }
    
    0 讨论(0)
  • 2020-11-22 06:14

    I know I'm late to the party, but I use this old set of functions by Marcos J. Montes for figuring out holidays and business days. He took the time to add an algorithm from 1876 for Easter and he added all the major US holidays. This can easily be updated for other countries.

    //Usage
    $days = 30;
    $next_working_date = nextWorkingDay($days, $somedate);
    
    //add date function
    function DateAdd($interval, $number, $date) {
    
        $date_time_array = getdate($date);
        //die(print_r($date_time_array));
    
        $hours = $date_time_array["hours"];
        $minutes = $date_time_array["minutes"];
        $seconds = $date_time_array["seconds"];
        $month = $date_time_array["mon"];
        $day = $date_time_array["mday"];
        $year = $date_time_array["year"];
    
        switch ($interval) {
    
            case "yyyy":
                $year+=$number;
                break;
            case "q":
                $year+=($number*3);
                break;
            case "m":
                $month+=$number;
                break;
            case "y":
            case "d":
            case "w":
                $day+=$number;
                break;
            case "ww":
                $day+=($number*7);
                break;
            case "h":
                $hours+=$number;
                break;
            case "n":
                $minutes+=$number;
                break;
            case "s":
                $seconds+=$number; 
                break;            
        }
        //      echo "day:" . $day;
        $timestamp= mktime($hours,$minutes,$seconds,$month,$day,$year);
        return $timestamp;
    }
    
    // the following function get_holiday() is based on the work done by
    // Marcos J. Montes
    function get_holiday($year, $month, $day_of_week, $week="") {
        if ( (($week != "") && (($week > 5) || ($week < 1))) || ($day_of_week > 6) || ($day_of_week < 0) ) {
            // $day_of_week must be between 0 and 6 (Sun=0, ... Sat=6); $week must be between 1 and 5
            return FALSE;
        } else {
            if (!$week || ($week == "")) {
                $lastday = date("t", mktime(0,0,0,$month,1,$year));
                $temp = (date("w",mktime(0,0,0,$month,$lastday,$year)) - $day_of_week) % 7;
            } else {
                $temp = ($day_of_week - date("w",mktime(0,0,0,$month,1,$year))) % 7;
            }
    
            if ($temp < 0) {
                $temp += 7;
            }
    
            if (!$week || ($week == "")) {
                $day = $lastday - $temp;
            } else {
                $day = (7 * $week) - 6 + $temp;
            }
            //echo $year.", ".$month.", ".$day . "<br><br>";
            return format_date($year, $month, $day);
        }
    }
    
    function observed_day($year, $month, $day) {
        // sat -> fri & sun -> mon, any exceptions?
        //
        // should check $lastday for bumping forward and $firstday for bumping back,
        // although New Year'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;
    }
    
    0 讨论(0)
  • 2020-11-22 06:15

    My version based on the work by @mcgrailm... tweaked because the report needed to be reviewed within 3 business days, and if submitted on a weekend, the counting would start on the following Monday:

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

    And working out the difference of two dates in terms of business days:

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

    As a note, everyone who is using 86400, or 24*60*60, please don't... your forgetting time changes from winter/summer time, where a day it not exactly 24 hours. While it's a little slower the strtotime('+1 day', $timestamp), it much more reliable.

    0 讨论(0)
  • 2020-11-22 06:15

    I had this same need i started with bobbin's first example and ended up with this

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

    hth someone

    0 讨论(0)
提交回复
热议问题