2012-07-09 18 views
4

我有一個函數返回兩個日期之間的差別,但是我需要計算出的差異在工作時間,假設週一至週五(上午9:00至下午5:30):calc下2日期間的工作時間在PHP

//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 3時58分58秒
日期2 = 2012-03-22 11點29分16秒

是否有這樣做的一個簡單的方法,即 - 計算一週內工作時間的百分比並將差值除以我們荷蘭國際集團上述功能 - 我打周圍的這個想法,得到了一些很奇怪的數字...

還是有更好的辦法....?

+0

你不能做一週的比例:如果你有一個完整的星期六和星期日,那就是工作周的0%,大概是一週的29%。 要做到這一點的最快方法是找出工作的全天數,然後計算這些全天工作前後的部分天數。 – Jerome 2012-07-09 14:46:54

回答

3

本例中使用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

參考

+0

這會對PHPgolf有很大的挑戰。 – 2013-12-19 14:27:04

+0

什麼功能需要5.3.6? – 2013-12-19 22:15:49

+0

PHPgolf的PHP版本:5.3.3-phpGolf http://www.phpgolf.org/doc從2010年7月起! 3年半過時; – 2013-12-19 22:23:55

2

下面是我想出。

我的解決方案檢查原始日期的開始和結束時間,並根據工作日的實際開始和結束時間對其進行調整(如果原始開始時間在工作開始時間之前,則將其設置爲後者)。

在完成開始和結束時間後,比較時間以檢索DateInterval差異,計算總天數,小時數等等。然後檢查日期範圍是否有任何週末天數,如果找到,總差額從差異中減少。

最後,作爲評價的時間被計算。:)

歡呼到John鼓舞人心的一些解決方案,特別是DatePeriod檢查週末。

金星給誰打破這個;如果有人發現漏洞,我會很樂意更新!


金星給自己,我打破了它!是的,週末仍然是越野車(嘗試從週六下午4點開始,週一下午1點結束)。我征服你,工作時間問題!

忍者編輯#2:如果週末出現問題,我想通過將開始和結束時間恢復到最近的相應工作日來照顧週末的錯誤。測試了一些日期範圍(如預期的那樣,在同一個週末的酒吧上開始和結束),取得了很好的效果。我並不完全相信這是儘可能的優化/簡單,但至少現在它效果更好。


// 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); 
+0

這兩個答案都是錯誤的,有人可以正確回答這個問題 – user794846 2014-06-30 14:48:53

+0

不是這樣的態度 – 2014-06-30 17:48:38

+0

@Cryode這是我找到的唯一代碼它可以100%的時間工作(據我所知),在這個問題上的所有其他答案和許多其他人都在某些日期範圍內進行了barfed(例如'2017-01-24 00 :00:00' - '2017-01-30 09:45:00'在任何其他答案上都不起作用!)。謝謝! – superphonic 2017-02-17 14:07:35

1

正如那句老話:「如果你想要的東西做對自己動手」。不是說這是最佳的,但它至少會爲我返回正確的小時數。

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;