Calculate working hours between two dates in PHP

I have a function to return the difference between the two dates, however I need to understand the difference in working hours, from Monday to Friday (from 9:00 to 17: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); } 

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

Is there an easy way to do this, i.e. calculate the percentage of working time per week and share the difference using the above function - I played with this idea and got some very strange numbers ...

Or is there a better way ...?

+4
source share
3 answers

This example uses PHP, built into DateTime classes, to do date math. As I approached this, it was necessary to start by counting the number of full working days between two dates, and then multiply this by 8 (see Notes). He then receives hours worked on partial days, and adds them to the total hours worked. Including this in a function would be fairly simple.

Notes:

  • The timestamp does not count. But you already know how to do it.
  • Does not handle holidays. (This can be easily added using the holidays array and adding it to where you filter Saturday and Sunday).
  • Requires PHP 5.3.6 +
  • Assumes an 8-hour work day. If employees do not accept lunch, replace $hours = $days * 8; at $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; 

Look in action

Link

+3
source

Here is what I came up with.

My solution checks the start and end time of the original dates and sets them in accordance with the actual start and end time of the working day (if the original start time is before the opening of the working time, it sets it to the last).

After that, for both the beginning and the end, the time is compared to extracting DateInterval diff, calculating the total days, hours, etc. Then, the date range is checked for any weekend, and if it is found, one common day is reduced from the difference.

Finally, the hours are calculated as commented out. :)

Welcomes John for inspiring some of these solutions, especially DatePeriod for the weekend.

A golden star for all who violate this; I will be happy to update if anyone finds a loophole!


Gold star for myself, I broke it! Yes, the weekend still doesn’t work (try starting at 16:00 on Saturday and ending at 13:00 on Monday). I will defeat you, the problem of working hours!

Ninja edit # 2: I think I took care of the weekend errors by returning the start and end time to the very last relevant weekday if they fall on the weekend. Got good results after testing several date ranges (starting and ending on the same output bars, as expected). I am not entirely convinced that it is as optimized / simple as it could be, but at least now it works better.


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

As the old saying goes, "if you want something done right, do it yourself." Not to say that this is optimal, but at least it returned me the correct number of hours.

 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("Ymd 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("Ymd 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; 
+1
source

All Articles