代码之家  ›  专栏  ›  技术社区  ›  Tatu Ulmanen

从给定日期中找出前三个工作日

  •  5
  • Tatu Ulmanen  · 技术社区  · 15 年前

    我需要从给定的日期中找出前三个工作日,忽略周末和假日。这本身并不是一项艰巨的任务,但我要做的方式似乎过于复杂,所以我想我应该先征求你的意见。

    为了让事情更有趣,让我们把这当成一场比赛。我将提供300美元作为奖励,奖励那些提出符合本规范的最短、最干净的解决方案的人:

    • 编写一个函数,返回给定日期前三个工作日
    • 该函数接受一个参数,即日期 Y-m-d 格式
    • 函数返回一个数组,数组中有三个日期 年-月-日 格式,从最早到最新排序。

    额外费用:

    • 函数还可以找到 除前三个工作日外,还有三个工作日

    $holidays = array(
        '2010-01-01',
        '2010-01-06',
        '2010-04-02',
        '2010-04-04',
        '2010-04-05',
        '2010-05-01',
        '2010-05-13',
        '2010-05-23',
        '2010-06-26',
        '2010-11-06',
        '2010-12-06',
        '2010-12-25',
        '2010-12-26'
    );
    

    请注意,在实际场景中,假日不是硬编码的,而是来自 get_holidays($year) 功能。如果你愿意的话,你可以在你的答案中包括/使用它。


    注意

    如果您使用固定的日长(如86400秒)从一天跳到另一天,您将遇到夏令时的问题。使用 strtotime('-1 day', $timestamp)

    这个问题的一个例子是:

    http://codepad.org/uSYiIu5w


    最终解决方案

    strtotime last weekday . 从传递的计数中检测方向,如果为负,则向后搜索,如果为正则向前搜索:

    function working_days($date, $count) {
    
        $working_days = array();
        $direction    = $count < 0 ? 'last' : 'next';
        $holidays     = get_holidays(date("Y", strtotime($date)));
    
        while(count($working_days) < abs($count)) {
            $date = date("Y-m-d", strtotime("$direction weekday", strtotime($date)));
            if(!in_array($date, $holidays)) {
                $working_days[] = $date;
            }
        }
    
        sort($working_days);
        return $working_days;
    }
    
    12 回复  |  直到 15 年前
        1
  •  8
  •   Keith Minkler    15 年前

    function last_working_days($date, $backwards = true)
    {
        $holidays = get_holidays(date("Y", strtotime($date)));
    
        $working_days = array();
    
        do
        {
            $direction = $backwards ? 'last' : 'next';
            $date = date("Y-m-d", strtotime("$direction weekday", strtotime($date)));
            if (!in_array($date, $holidays))
            {
                $working_days[] = $date;
            }
        }
        while (count($working_days) < 3);
    
        return $working_days;
    }
    
        2
  •  10
  •   Wireblue    15 年前

    这应该可以做到:

        // Start Date must be in "Y-m-d" Format
        function LastThreeWorkdays($start_date) {
            $current_date = strtotime($start_date);
            $workdays = array();
            $holidays = get_holidays('2010');
    
            while (count($workdays) < 3) {
                $current_date = strtotime('-1 day', $current_date);
    
                if (in_array(date('Y-m-d', $current_date), $holidays)) {    
                    // Public Holiday, Ignore.
                    continue;
                }
    
                if (date('N', $current_date) < 6) {
                    // Weekday. Add to Array.
                    $workdays[] = date('Y-m-d', $current_date);
                }
            }
    
            return array_reverse($workdays);
        }
    

    我已经在get\u holidays()函数中进行了硬编码,但是我相信您会理解这个想法,并对其进行适当的调整。其余的都是工作代码。

        3
  •  3
  •   Emil Vikström    15 年前

    通过 true 作为第二个论点,要在时间上向前而不是向后。我还编辑了这个函数,如果你将来想的话,可以允许超过三天的时间。

    function last_workingdays($date, $forward = false, $numberofdays = 3) {
            $time = strtotime($date);
            $holidays = get_holidays();
            $found = array();
            while(count($found) < $numberofdays) {
                    $time -= 86400 * ($forward?-1:1);
                    $new = date('Y-m-d', $time);
                    $weekday = date('w', $time);
                    if($weekday == 0 || $weekday == 6 || in_array($new, $holidays)) {
                            continue;
                    }
                    $found[] = $new;
            }
            if(!$forward) {
                    $found = array_reverse($found);
            }
            return $found;
    }
    
        4
  •  3
  •   Daniel Egeberg    15 年前

    function get_workdays($date, $num = 3, $next = false)
    {
        $date = DateTime::createFromFormat('Y-m-d', $date);
        $interval = new DateInterval('P1D');
        $holidays = array();
    
        $res = array();
        while (count($res) < $num) {
            $date->{$next ? 'add' : 'sub'}($interval);
    
            $year = (int) $date->format('Y');
            $formatted = $date->format('Y-m-d');
    
            if (!isset($holidays[$year]))
                $holidays[$year] = get_holidays($year);
    
            if ($date->format('N') <= 5 && !in_array($formatted, $holidays[$year]))
                $res[] = $formatted;
        }
        return $next ? $res : array_reverse($res);
    }
    
        5
  •  1
  •   acm    15 年前

    把86400换成了 -1 day 虽然我不完全明白这是否真的是个问题。

    对原来的函数做了一些修改,但基本相同。

    // -----------------------
    // Previous 3 working days # this is almost the same that someone already posted
    function getWorkingDays($date){
        $workdays = array();
        $holidays = getHolidays();
        $date     = strtotime($date);
    
        while(count($workdays) < 3){
            $date = strtotime("-1 day", $date);
    
            if(date('N',$date) < 6 && !in_array(date('Y-m-d',$date),$holidays))
                $workdays[] = date('Y-m-d',$date);
        }
    
        krsort($workdays);
        return $workdays;
    }
    // --------------------------------
    // Previous and Next 3 working days
    function getWorkingDays2($date){
        $workdays['prev'] = $workdays['next'] = array();
        $holidays = getHolidays();
        $date     = strtotime($date);
    
        $start_date = $date;
        while(count($workdays['prev']) < 3){
            $date = strtotime("-1 day", $date);
    
            if(date('N',$date) < 6 && !in_array(date('Y-m-d',$date),$holidays))
                $workdays['prev'][] = date('Y-m-d',$date);
        }
        $date = $start_date;
        while(count($workdays['next']) < 3){
            $date = strtotime("+1 day", $date);
    
            if(date('N',$date) < 6 && !in_array(date('Y-m-d',$date),$holidays))
                $workdays['next'][] = date('Y-m-d',$date);
        }
    
        krsort($workdays['prev']);
        return $workdays;
    }
    
    function getHolidays(){
        $holidays = array(
            '2010-01-01', '2010-01-06',
            '2010-04-02', '2010-04-04', '2010-04-05',
            '2010-05-01', '2010-05-13', '2010-05-23',
            '2010-06-26',
            '2010-11-06',
            '2010-12-06', '2010-12-25', '2010-12-26'
        );
        return $holidays;
    }
    
    echo '<pre>';
    print_r( getWorkingDays( '2010-04-04' ) );
    print_r( getWorkingDays2( '2010-04-04' ) );
    echo '</pre>';
    

    输出:

    Array
    (
        [2] => 2010-03-30
        [1] => 2010-03-31
        [0] => 2010-04-01
    )
    Array
    (
        [next] => Array
            (
                [0] => 2010-04-06
                [1] => 2010-04-07
                [2] => 2010-04-08
            )
    
        [prev] => Array
            (
                [2] => 2010-03-30
                [1] => 2010-03-31
                [0] => 2010-04-01
            )
    
    )
    
        6
  •  1
  •   Austin Hyde    15 年前

    我的做法是:

    function business_days($date) {
        $out = array();
        $day = 60*60*24;
    
        //three back
        $count = 0;
        $prev = strtotime($date);
        while ($count < 3) {
            $prev -= $day;
            $info = getdate($prev);
            $holidays = get_holidays($info['year']);
            if ($info['wday'] == 0 || $info['wday'] == 6 || in_array($date,$holidays))
                    continue;
            else {
                $out[] = date('Y-m-d',$prev);
                $count++;
            }
        }
    
        $count = 0;
        $next = strtotime($date);
        while ($count < 3) {
            $next += $day;
            $info = getdate($next);
            $holidays = get_holidays($info['year']);
            if ($info['wday']==0 || $info['wday']==6 || in_array($date,$holidays))
                    continue;
            else {
                $out[] = date('Y-m-d',$next);
                $count++;
            }
        }
    
        sort($out);
    
        return $out;
    }
    
        7
  •  1
  •   acm    15 年前

    function getWorkDays($date){
        list($year,$month,$day) = explode('-',$date);
        $holidays = getHolidays();
        $dates    = array();
    
        while(count($dates) < 3){
            $newDate = date('Y-m-d',mktime(0,0,0,$month,--$day,$year));
            if(date('N',strtotime($newDate)) < 6 && !in_array($newDate,$holidays))
                $dates[] = $newDate;
        }
    
        return array_reverse($dates);
    }
    
    print_r(getWorkDays('2010-12-08'));
    

    Array
    (
        [0] => 2010-12-02
        [1] => 2010-12-03
        [2] => 2010-12-07
    )
    
        8
  •  0
  •   Mark Baker    15 年前

    如果您查看中的WORKDAYS函数 PHPExcel ,您将找到如何编写此类函数的示例

        9
  •  0
  •   ktharsis    15 年前

    试试这个(警告-我没有权限测试这个,所以请纠正任何语法错误)。

    function LastThreeWorkdays($start_date) { 
        $startdateseed = strtotime($start_date); 
        $workdays = array(); 
        $holidays = get_holidays('2010'); 
    
        for ($counter = -1; $counter >= -10; $counter--) 
          if (date('N', $current_date = strtotime($counter.' day', $startdateseed)) < 6) $workdays[] = date('Y-m-d', $currentdate);
    
        return array_slice(array_reverse(array_diff($workdays, $holidays)), 0, 3);
    }
    

    基本上创建一个日期“块”,然后使用数组diff从中删除假日。只返回前(后)三项。显然,它需要一个微乎其微的存储空间和时间来计算比以前的答案,但代码要短得多。

    代码也可以“展开”以使某些技巧更易于阅读。总体上展示了一些更好的PHP函数-也可以与其他想法结合。

        10
  •  0
  •   artlung    15 年前
    /**
      * @param $currentdate like 'YYYY-MM-DD'
      * @param $n number of workdays to return
      * @param $direction 'previous' or 'next', default is 'next'
      **/
    function adjacentWorkingDays($currentdate, $n, $direction='next') {
        $sign = ($direction == 'previous') ? '-' : '+';
        $workdays = array();
        $holidays = get_holidays();
        $i = 1;
        while (count($workdays) < $n) {
            $dateinteger = strtotime("{$currentdate} {$sign}{$i} days");
            $date = date('Y-m-d', $dateinteger);
            if (!in_array($date, $holidays) && date('N', $dateinteger) < 6) {
                $workdays[] = $date;
            }
            $i++;
        }
        return $workdays;
    }
    
    // you pass a year into get_holidays, make sure folks
    // are accounting for the fact that adjacent holidays
    // might cross a year boundary
    function get_holidays() {
        $holidays = array(
            '2010-01-01',
            '2010-01-06',
            '2010-04-02',
            '2010-04-04',
            '2010-04-05',
            '2010-05-01',
            '2010-05-13',
            '2010-05-23',
            '2010-06-26',
            '2010-11-06',
            '2010-12-06',
            '2010-12-25',
            '2010-12-26'
        );
        return $holidays;
    }
    

    在这些函数中,我们使用 adjacentWorkingDays() 功能:

    // next $n working days, in ascending order
    function nextWorkingDays($date, $n) {
        return adjacentWorkingDays($date, $n, 'next');
    }
    
    // previous $n workind days, in ascending order
    function previousWorkingDays($date, $n) {
        return array_reverse(adjacentWorkingDays($date, $n, 'previous'));
    }
    

    print "<pre>";
    print_r(nextWorkingDays('2010-06-24', 3));
    print_r(previousWorkingDays('2010-06-24', 3));
    print "<pre>";
    

    Array
    (
        [0] => 2010-06-25
        [1] => 2010-06-28
        [2] => 2010-06-29
    )
    Array
    (
        [0] => 2010-06-21
        [1] => 2010-06-22
        [2] => 2010-06-23
    )
    
        11
  •  0
  •   alexanderpas    15 年前

    /**
     * Helper function to handle year overflow
     */
    function isHoliday($date) {
      static $holidays = array(); // static cache
      $year = date('Y', $date);
    
      if(!isset($holidays["$year"])) {
        $holidays["$year"] = get_holidays($year);
      }
    
      return in_array(date('Y-m-d', $date), $holidays["$year"]);
    }
    
    /**
     * Returns adjacent working days (by default: the previous three)
     */
    function adjacentWorkingDays($start_date, $limit = 3, $direction = 'previous') {
      $current_date = strtotime($start_date);
      $direction = ($direction === 'next') ? 'next' : 'previous'; // sanity
      $workdays = array();
    
      // no need to verify the count before checking the first day.
      do {
        // using weekday here skips weekends.
        $current_date = strtotime("$direction weekday", $current_date);
        if (!isHoliday()) {
          // not a public holiday.
          $workdays[] = date('Y-m-d', $current_date);
        }
      } while (count($workdays) < $limit)
    
      return array_reverse($workdays);
    }
    
        12
  •  0
  •   Josh Leitzel    15 年前

    这是我的照片。如果您在年初输入日期,此功能(与大多数其他发布的功能不同)不会失败。如果你只打电话给 get_holidays 函数对一年执行时,生成的数组可能包含上一年的假日日期。我的解决方案将调用 如果我们回到上一年。

    function get_working_days($date)
    {
        $date_timestamp = strtotime($date);
        $year = date('Y', $date_timestamp);
        $holidays = get_holidays($year);
        $days = array();
    
        while (count($days) < 3)
        {
            $date_timestamp = strtotime('-1 day', $date_timestamp);
            $date = date('Y-m-d', $date_timestamp);         
    
            if (!in_array($date, $holidays) && date('N', $date_timestamp) < 6)
                $days[] = $date;
    
    
            $year2 = date('Y', $date_timestamp);
            if ($year2 != $year)
            {
                $holidays = array_merge($holidays, get_holidays($year2));
                $year = $year2;
            }
        }
    
        return $days;
    }
    
    推荐文章