【问题标题】:calc the working hours between 2 dates in PHP计算PHP中2个日期之间的工作时间
【发布时间】:2012-07-08 22:34:34
【问题描述】:

我有一个函数可以返回两个日期之间的差异,但是我需要计算出工作时间的差异,假设星期一到星期五(上午 9 点到下午 5 点 30 分):

//DATE DIFF FUNCTION
// Set timezone
date_default_timezone_set("GMT");

// Time format is UNIX timestamp or
// PHP strtotime compatible strings
function dateDiff($time1, $time2, $precision = 6) {
    // If not numeric then convert texts to unix timestamps
    if (!is_int($time1)) {
        $time1 = strtotime($time1);
    }
    if (!is_int($time2)) {
        $time2 = strtotime($time2);
    }

    // If time1 is bigger than time2
    // Then swap time1 and time2
    if ($time1 > $time2) {
        $ttime = $time1;
        $time1 = $time2;
        $time2 = $ttime;
    }

    // Set up intervals and diffs arrays
    $intervals = array('year','month','day','hour','minute','second');
    $diffs = array();

    // Loop thru all intervals
    foreach ($intervals as $interval) {
        // Set default diff to 0
        $diffs[$interval] = 0;
        // Create temp time from time1 and interval
        $ttime = strtotime("+1 " . $interval, $time1);
        // Loop until temp time is smaller than time2
        while ($time2 >= $ttime) {
            $time1 = $ttime;
            $diffs[$interval]++;
            // Create new temp time from time1 and interval
            $ttime = strtotime("+1 " . $interval, $time1);
        }
    }

    $count = 0;
    $times = array();
    // Loop thru all diffs
    foreach ($diffs as $interval => $value) {
        // Break if we have needed precission
        if ($count >= $precision) {
            break;
        }
        // Add value and interval 
        // if value is bigger than 0
        if ($value > 0) {
            // Add s if value is not 1
            if ($value != 1) {
                $interval .= "s";
            }
            // Add value and interval to times array
            $times[] = $value . " " . $interval;
            $count++;
        }
    }

    // Return string with times
    return implode(", ", $times);
}

日期 1 = 2012-03-24 03:58:58
日期 2 = 2012-03-22 11:29:16

有没有一种简单的方法可以做到这一点,即 - 计算一周中的工作时间百分比并使用上述函数除以差异 - 我已经尝试过这个想法并得到了一些非常奇怪的数字......

或者有没有更好的方法....?

【问题讨论】:

  • 你不能做每周百分比的事情:如果你有一个完整的周六和周日,那就是一周工作的 0%,大约是一周的 29%。最快的方法是计算出全天的工作天数,然后计算这些全天工作前后的部分天数。

标签: php date time date-math


【解决方案1】:

本示例使用 PHP 内置的 DateTime 类来进行日期数学运算。我的方法是从计算两个日期之间的完整工作日数开始,然后将其乘以 8(见注释)。然后它会获取部分工作日的工作时间并将它们添加到总工作时间中。把它变成一个函数是相当简单的。

注意事项:

  • 不考虑时间戳。但您已经知道该怎么做。
  • 不处理假期。 (可以通过使用一系列假期并将其添加到您过滤掉周六和周日的位置来轻松添加)。
  • 需要 PHP 5.3.6+
  • 假设每天工作 8 小时。如果员工不吃午饭,则将$hours = $days * 8; 更改为$hours = $days * 8.5;

.

<?php
// Initial datetimes
$date1 = new DateTime('2012-03-22 11:29:16');
$date2 = new DateTime('2012-03-24 03:58:58');

// Set first datetime to midnight of next day
$start = clone $date1;
$start->modify('+1 day');
$start->modify('midnight');

// Set second datetime to midnight of that day
$end = clone $date2;
$end->modify('midnight');

// Count the number of full days between both dates
$days = 0;

// Loop through each day between two dates
$interval = new DateInterval('P1D');
$period = new DatePeriod($start, $interval, $end);
foreach ($period as $dt) {
    // If it is a weekend don't count it
    if (!in_array($dt->format('l'), array('Saturday', 'Sunday'))) {
        $days++;
    }
}

// Assume 8 hour workdays
$hours = $days * 8;

// Get the number of hours worked on the first day
$date1->modify('5:30 PM');
$diff = $date1->diff($start);
$hours += $diff->h;

// Get the number of hours worked the second day
$date1->modify('8 AM');
$diff = $date2->diff($end);
$hours += $diff->h;

echo $hours;

See it in action

参考

【讨论】:

  • 什么功能需要 5.3.6?
  • PHPgolf 的 PHP 版本:5.3.3-phpGolf phpgolf.org/doc 这是从 2010 年 7 月开始的! 3 1/2 年过时 ;o
  • @TheodoreR.Smith 日期时间功能。如果您在早期版本的 PHP 5.3 中运行它,您将得到不正确的结果。点击“查看实际效果”链接了解我的意思。
  • 我尝试在 bove 解决方案中添加固定假期。我试过: if($date > $startofday && $date format('n'), array(5,6,7))){ $count++; } 但它只给出天数。尝试改用“j”,但无法找出正确的格式。我试过: if($date > $startofday && $date format('j'), array(5,6,7,9))){ $count++; } 表示第 5、第 6、第 7、第 9。有什么建议吗?
【解决方案2】:

这是我想出的。

我的方案是检查原日期的起止时间,并根据工作日的实际起止时间进行调整(如果原来的开始时间在上班时间之前,则设置为后者) .

对开始时间和结束时间都完成此操作后,将比较时间以检索 DateInterval 差异,计算总天数、总小时数等。然后检查日期范围的任何周末,如果找到,从差异中减少了总天数。

最后,按照评论计算小时数。 :)

感谢 John 启发了这个解决方案,特别是 DatePeriod 检查周末。

任何打破这一点的人都会获得金星奖;如果有人发现漏洞,我会很乐意更新!


给我自己的金星,我把它弄坏了! 是的,周末仍然有问题(尝试从周六下午 4 点开始,到周一下午 1 点结束)。我征服你,工作时间问题!

Ninja 编辑 #2: 我想我已经解决了周末的错误,如果它们是在周末,我会将开始和结束时间恢复到最近的相应工作日。在测试了几个日期范围后得到了很好的结果(正如预期的那样,在同一个周末的 barfs 开始和结束)。我不完全相信这是尽可能优化/简单,但至少现在效果更好。


// Settings
$workStartHour = 9;
$workStartMin = 0;
$workEndHour = 17;
$workEndMin = 30;
$workdayHours = 8.5;
$weekends = ['Saturday', 'Sunday'];
$hours = 0;

// Original start and end times, and their clones that we'll modify.
$originalStart = new DateTime('2012-03-22 11:29:16');
$start = clone $originalStart;

// Starting on a weekend? Skip to a weekday.
while (in_array($start->format('l'), $weekends))
{
    $start->modify('midnight tomorrow');
}

$originalEnd = new DateTime('2012-03-24 03:58:58');
$end = clone $originalEnd;

// Ending on a weekend? Go back to a weekday.
while (in_array($end->format('l'), $weekends))
{
    $end->modify('-1 day')->setTime(23, 59);
}

// Is the start date after the end date? Might happen if start and end
// are on the same weekend (whoops).
if ($start > $end) throw new Exception('Start date is AFTER end date!');

// Are the times outside of normal work hours? If so, adjust.
$startAdj = clone $start;

if ($start < $startAdj->setTime($workStartHour, $workStartMin))
{
    // Start is earlier; adjust to real start time.
    $start = $startAdj;
}
else if ($start > $startAdj->setTime($workEndHour, $workEndMin))
{
    // Start is after close of that day, move to tomorrow.
    $start = $startAdj->setTime($workStartHour, $workStartMin)->modify('+1 day');
}

$endAdj = clone $end;

if ($end > $endAdj->setTime($workEndHour, $workEndMin))
{
    // End is after; adjust to real end time.
    $end = $endAdj;
}
else if ($end < $endAdj->setTime($workStartHour, $workStartMin))
{
    // End is before start of that day, move to day before.
    $end = $endAdj->setTime($workEndHour, $workEndMin)->modify('-1 day');
}

// Calculate the difference between our modified days.
$diff = $start->diff($end);

// Go through each day using the original values, so we can check for weekends.
$period = new DatePeriod($start, new DateInterval('P1D'), $end);

foreach ($period as $day)
{
    // If it's a weekend day, take it out of our total days in the diff.
    if (in_array($day->format('l'), ['Saturday', 'Sunday'])) $diff->d--;
}

// Calculate! Days * Hours in a day + hours + minutes converted to hours.
$hours = ($diff->d * $workdayHours) + $diff->h + round($diff->i / 60, 2);

【讨论】:

  • 这两个答案都是错误的,请有人正确回答这个问题。
  • 不是那种态度。
  • @Cryode 这是我发现的唯一一个 100% 有效的代码(据我所知)。关于这个问题的所有其他答案以及许多其他答案都在某些日期范围内大喊大叫(例如2017-01-24 00:00:00 - 2017-01-30 09:45:00 对任何其他答案都不起作用!)。谢谢!
  • 刮一下,我刚刚弄坏了它。 2017-02-08 19:00:00 - 2017-02-08 22:00:00 16 小时后出结果
  • @superphonic 我忘记了这个答案。为您找到另一个问题的金星!如果您找到解决方案,请告诉我。 :)
【解决方案3】:

正如那句老话所说:“如果你想把事情做好,就自己做吧”。并不是说这是最佳的,但它至少为我返回了正确的小时数。

function biss_hours($start, $end){

    $startDate = new DateTime($start);
    $endDate = new DateTime($end);
    $periodInterval = new DateInterval( "PT1H" );

    $period = new DatePeriod( $startDate, $periodInterval, $endDate );
    $count = 0;

      foreach($period as $date){

           $startofday = clone $date;
           $startofday->setTime(8,30);

           $endofday = clone $date;
           $endofday->setTime(17,30);

    if($date > $startofday && $date <= $endofday && !in_array($date->format('l'), array('Sunday','Saturday'))){

        $count++;
    }

}

//Get seconds of Start time
$start_d = date("Y-m-d H:00:00", strtotime($start));
$start_d_seconds = strtotime($start_d);
$start_t_seconds = strtotime($start);
$start_seconds = $start_t_seconds - $start_d_seconds;

//Get seconds of End time
$end_d = date("Y-m-d H:00:00", strtotime($end));
$end_d_seconds = strtotime($end_d);
$end_t_seconds = strtotime($end);
$end_seconds = $end_t_seconds - $end_d_seconds;

$diff = $end_seconds-$start_seconds;

if($diff!=0):
    $count--;
endif;

$total_min_sec = date('i:s',$diff);

return $count .":".$total_min_sec;
}

$start = '2014-06-23 12:30:00';
$end = '2014-06-27 15:45:00';

$go = biss_hours($start,$end);
echo $go;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-03
    • 1970-01-01
    相关资源
    最近更新 更多