【问题标题】:PHP - subtract dates on business days and working hoursPHP - 减去工作日和工作时间的日期
【发布时间】:2012-12-01 02:14:22
【问题描述】:

我想计算两个日期之间的工作时间。所以我需要忽略周六/周日,我的工作时间是上午 9 点到下午 6 点(9:00 到 18:00)。

我在网上搜索并尝试了一些变体,但似乎没有任何效果。有谁可以帮忙?

我一直在看这个链接: Calculating working hours between two dates subtract non working hours and days from two given times in php calc the working hours between 2 dates in PHP

但是,或者他们没有得到答案,或者答案提供了它不起作用。

感谢您的帮助

【问题讨论】:

    标签: php datetime


    【解决方案1】:

    我根据自己的需要在 web 和 stackoverflow 上调整了一些功能。他们来了。

    调用脚本:

    date_default_timezone_set("Europe/Lisbon");
    $startDate=strtotime('2012-11-30 00:15:33');
    $endDate = strtotime('2012-12-05 10:15:00');
    
    echo "Working hours from create date until due date -> ".seconds2human(work_hours_diff($startDate,$endDate));
    

    人类可读的秒数(改编自http://snippetsofcode.wordpress.com/2012/08/25/php-function-to-convert-seconds-into-human-readable-format-months-days-hours-minutes/):

    function seconds2human($ss) {
        $s = $ss%60;
        $m = floor(($ss%3600)/60);
        $h = floor(($ss)/3600);
    
        return "$h hours, $m minutes, $s seconds";
    }
    

    使用工作日和工作时间的子跟踪日期(改编自Calculating working hours between two dates):

    function work_hours_diff($date1,$date2) {
        if ($date1>$date2) { 
            $tmp=$date1; 
            $date1=$date2; 
            $date2=$tmp; 
            unset($tmp); 
            $sign=-1; 
        } else $sign = 1;
        if ($date1==$date2) return 0;
    
        $days = 0;
        $working_days = array(1,2,3,4,5); // Monday-->Friday
        $working_hours = array(9, 17.5); // from 9:00 to 17:30 (8.5 hours)
        $current_date = $date1;
    
        $beg_h = floor($working_hours[0]); 
        $beg_m = ($working_hours[0]*60)%60;
        $end_h = floor($working_hours[1]); 
        $end_m = ($working_hours[1]*60)%60;
    
        //In case date1 is on same day of date2
        if (mktime(0,0,0,date('n', $date1), date('j', $date1), date('Y', $date1))==mktime(0,0,0,date('n', $date2), date('j', $date2), date('Y', $date2))) {
            //If its not working day, then return 0
            if (!in_array(date('w', $date1), $working_days)) return 0;
    
            $date0 = mktime($beg_h, $beg_m, 0, date('n', $date1), date('j', $date1), date('Y', $date1));
            $date3 = mktime($end_h, $end_m, 0, date('n', $date1), date('j', $date1), date('Y', $date1));
    
            if ($date1<$date0) {
                if ($date2<$date0) return 0;
                $date1 = $date0;
                if ($date2>$date3) $date2=$date3;
                return $date2-$date1;
            }
            if ($date1>$date3) return 0;
            if ($date2>$date3) $date2=$date3;
            return $date2-$date1;
        }
    
        //setup the very next first working time stamp
        if (!in_array(date('w',$current_date) , $working_days)) {
            // the current day is not a working day
    
            // the current time stamp is set at the beginning of the working day
            $current_date = mktime( $beg_h, $beg_m, 0, date('n',$current_date), date('j',$current_date), date('Y',$current_date) );
    
            // search for the next working day
            while ( !in_array(date('w',$current_date) , $working_days) ) {
                $current_date += 24*3600; // next day
            }
        } else {
            // check if the current timestamp is inside working hours
            $date0 = mktime( $beg_h, $beg_m, 0, date('n',$current_date), date('j',$current_date), date('Y',$current_date) );
            // it's before working hours, let's update it
            if ($current_date<$date0) $current_date = $date0;
    
            $date3 = mktime( $end_h, $end_m, 0, date('n',$current_date), date('j',$current_date), date('Y',$current_date) );
    
            if ($date3<$current_date) {
                // outch ! it's after working hours, let's find the next working day
                $current_date += 24*3600; // the day after
                // and set timestamp as the beginning of the working day
                $current_date = mktime( $beg_h, $beg_m, 0, date('n',$current_date), date('j',$current_date), date('Y',$current_date) );
                while ( !in_array(date('w',$current_date) , $working_days) ) {
                    $current_date += 24*3600; // next day
                }
            }
        }
    
        // so, $current_date is now the first working timestamp available...
    
        // calculate the number of seconds from current timestamp to the end of the working day
        $date0 = mktime( $end_h, $end_m, 0, date('n',$current_date), date('j',$current_date), date('Y',$current_date) );
        $seconds = $date0-$current_date;
    
        // calculate the number of days from the current day to the end day
    
        $date3 = mktime( $beg_h, $beg_m, 0, date('n',$date2), date('j',$date2), date('Y',$date2) );
        while ( $current_date < $date3 ) {
            if (in_array(date('w',$current_date) , $working_days) ) $days++; // it's a working day
            $current_date += 24*3600; // next day
        }
        $days--; //because we've already count the first day (in $seconds)
    
        // check if end's timestamp is inside working hours
        $date0 = mktime( $beg_h, $beg_m, 0, date('n',$date2), date('j',$date2), date('Y',$date2) );
        if ((!in_array(date('w', $date2), $working_days)) || ($date2 < $date0)) {
            // it's before, so nothing more !
        } else {
            // is it after ?
            $date3 = mktime( $end_h, $end_m, 0, date('n',$date2), date('j',$date2), date('Y',$date2) );
            if ($date2>$date3) $date2=$date3;
            // calculate the number of seconds from current timestamp to the final timestamp
            $tmp = $date2-$date0;
            $seconds += $tmp;
         }
    
        // calculate the working days in seconds
        $seconds += 3600*($working_hours[1]-$working_hours[0])*$days;
    
        return $sign * $seconds;
    }
    

    【讨论】:

    • 这不能正常工作。尝试从2013-09-13 16:30:002013-09-14 21:00:00 的示例;这只是一个工作日2013-09-13,所以结果应该是1hour,但它返回9.5hours
    • Nice catch... 让我们替换行 if ($date2
    • 我也放了 $current_date += 24*3600;验证后是否为工作日。再次复制整个函数并在您能记住的所有场景上进行测试。
    • 试试 2013-09-21 23:11:47 和 2013-09-22 00:09:25,注意这两天都在同一个周末,函数返回一整天。这给我带来了一些麻烦,但我想出的解决方案只是删除if ($days&gt;0) $days--;中的if。现在这可能会产生其他意想不到的结果,因此确认这确实是一个正确的解决方案会很好。
    • 不删除 if ($days>0) $days--;仅删除 if 条件。脚本已更新。但即使它可以工作,但它是一团糟,我保证当我有时间时,我会用更好的逻辑和架构从头开始制作自己的。
    【解决方案2】:

    我知道为时已晚,但我想这个问题还没有解决,我编写了一个代码,可以帮助其他人解决同样的问题。这段代码只是增加了工作日,所以它会跳过周六和周日的午餐时间。

    <?php 
    function recursivoHorarioComercial($data)
    {
    $hora =  date("H", strtotime($data));
    if($hora >= 18){
        $arrHorarios = array('18'=>'15','19'=>'14','20'=>'13','21'=>'12','22'=>'11','23'=>'10','00'=>'9','01'=>'8','02'=>'7',);
        $minutes = (60 * $arrHorarios[$hora]);
        $data = date("Y-m-d H:i:s", strtotime("+".$minutes." minutes", strtotime($data)));
    }
    $diaSemana = date("l", strtotime($data));
    if($diaSemana == "Saturday"){
        $extraMinutes = (2 * 24 * 60);
        $data = date("Y-m-d H:i:s", strtotime("+".$extraMinutes." minutes", strtotime($data)));
    } elseif($diaSemana == "Saturday"){
        $extraMinutes = (1 * 24 * 60);
        $data = date("Y-m-d H:i:s", strtotime("+".$extraMinutes." minutes", strtotime($data)));
    }
    $horaFim = date("H", strtotime($data));
    return $data;
    }
    function incrementMinutes($data,$minutes)
    {
    $hours = floor($minutes / 60);
    $minutes = ($minutes % 60);
    $hid = 8;
    $days = floor($hours/$hid);
    $horaINi =  date("H", strtotime($data));
    $hours = $hours - ($days * $hid);
    $data = date("Y-m-d H:i:s", strtotime("+".$minutes." minutes", strtotime($data)));
    $data = recursivoHorarioComercial($data);
    $data = date("Y-m-d H:i:s", strtotime("+".$hours." hours", strtotime($data)));
    $data = recursivoHorarioComercial($data);
    for ($i = 1; $i <= $days; $i++){
        $data = date("Y-m-d H:i:s", strtotime("+1 day", strtotime($data)));
        $data = recursivoHorarioComercial($data);
    }
    $horaFim = date("H", strtotime($data));
    if($horaINi <= 12 && $horaFim >= 12 ){
        $data = date("Y-m-d H:i:s", strtotime("+60 minutes", strtotime($data)));
    }
    return $data;
    }
    
    $date = "2016-04-06 09:00:00";
    $minutos =  24 * 60; //increase 24 hours
    echo "Now: ".$data."<br />";
    echo "<h2>Modified</h2>";
    echo incrementMinutes($date,$minutos)."<br />";
    
    
    ?>
    

    我刚刚测试了这段代码,它运行良好。我希望这可以帮助任何人。

    再见

    【讨论】:

      【解决方案3】:

      我知道我迟到了,但我试图找到答案并花了几个小时创建自己的答案,我并不是说它完美,如果有人想修改我的代码以使其更清晰,请随意。仍然有一些事情要改变,但它的工作原理。我已经使用 Carbon 来协助约会。

          function getWorkingHoursDifference($start,$end){
      
      
      // Declare Business Hours Start
      $BusinessHoursStart = '08:30:00';
      
      // Declare Business Hours Start
      $BusinessHoursEnd = '17:00:00';
      
      // Set Full Day Span // *Need to Calc from above
      $fullDayMinutes = 8.5*60;
      
      // Declare Exclusions
      $exclusions = ['Saturday', 'Sunday'];
      
      // Declare Start Time
      $TimeStart  = Carbon::parse($start);
      
      // Declare End Time
      $TimeEnd    = Carbon::parse($end);
      
      // Create Start Date
      $DateStart  = Carbon::parse(date_format($TimeStart, 'Y-m-d'));
      
      // Create End Date
      $DateEnd    = Carbon::parse(date_format($TimeEnd, 'Y-m-d'));
      
      // Create Period Span
      $period = CarbonPeriod::create($DateStart, $DateEnd);
      
      // Create Period Array
      $period  = $period->toArray();
      
      // Set Day Counter
      $dayCount = 0;
      
      // Set Loop Counter
      $loopCount = 0;
      
      // Total Minutes
      $totalMinutes = 0;
      
      
      foreach ($period as $date) {$dayCount++;}
      
          // Debugging
              //echo '<table class="table"><thead><th>LoopCount</th><th>Period Date</th><th>Day</th><th>Day Start</th><th>Day End</th><th>Minutes</th></thead>';
      
              foreach ($period as $date) {
      
              $dayOnly        = date_format($date, 'Y-m-d');
              $dayOnlyName    = date_format($date, 'l');
              $dayStart       = Carbon::parse($dayOnly.$BusinessHoursStart);
              $dayEnd         = Carbon::parse($dayOnly.$BusinessHoursEnd);
      
                      // Clean Minutes
                      $minutes = 0;
                      // Loop Counter
                      $loopCount++;
      
                      // Check Excluded Days
                      if(in_array($dayOnlyName, $exclusions)){$minutes = 0;}
      
                      // Check if its a single day
                      elseif($loopCount == 1 && $dayCount == 1){
                          if($TimeStart < $dayStart && $TimeEnd > $dayStart && $TimeEnd < $dayEnd){$minutes = $dayStart->diffInMinutes($TimeEnd);}
                          // If the time start is after business hours then 0
                          elseif($TimeStart > $dayEnd){$minutes = 0;}
                          // If the time start is after start of the day, but before closing hours
                          elseif($TimeStart > $dayStart){$minutes = $TimeStart->diffInMinutes($TimeEnd);}
      
                      }
      
                      // First Day
                      elseif($loopCount == 1){
                          // If the Time Start is before business hours, count as a full day.
                          if($TimeStart < $dayStart){$minutes = $fullDayMinutes;}
                          // If the time start is after business hours then 0
                          elseif($TimeStart > $dayEnd){$minutes = 0;}
                          // If the time start is after start of the day, but before closing hours
                          elseif($TimeStart > $dayStart){$minutes = $TimeStart->diffInMinutes($dayEnd);}
                      }
      
                      // Last Day (loopCounter Matches Total Day Counter)
                      elseif($loopCount == $dayCount){
                          // If the Time is before the start of the business day
                          if($TimeEnd< $dayStart){$minutes = 0;}
                          // If the End Time is after the day end then full day is counted
                          elseif($TimeEnd > $dayEnd){$minutes = $fullDayMinutes;}
                          // If the End time is after the start but before the end of business hours, work out the difference
                          elseif($TimeEnd > $dayStart){$minutes = $dayStart->diffInMinutes($TimeEnd);}
                      }
      
                      // All other days in the middle
                      else{$minutes = $fullDayMinutes;}
      
                      // Debugging
                      //echo '<tr><td>'.$loopCount.'</td><td>'.$date.'</td><td>'.$dayOnlyName.'</td><td>'.$dayStart.'</td><td>'.$dayEnd.'</td><td>'.$minutes.'</td></tr>';
      
                      $totalMinutes = $totalMinutes + $minutes;
      
                  }
      
      
      
              //echo '</table>';
      
              return $totalMinutes;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-05-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-30
        • 2019-05-04
        相关资源
        最近更新 更多