Calculate Business Hours using Carbon, PHP in Laravel
I have recently come across a situation where in I had to calculate the business hours between two datetime’s. I started writing a helper function in Laravel which takes in two Carbon instances as arguments and returns the work hours considering 0800 – 1700 as work hours, Weekends are holidays and also takes in holiday dates which will be considered and not counted.
This post helps only to those who have basic idea of Laravel. If you want me to elaborate I would be glad to but I need someone to ask me for it. As the blog is left out with 0 comments, I want you to talk and ask me for it.
The function
<?php use Carbon\Carbon; function getDiffHours($from,$to) { if(!isset($to)) { $to = Carbon::now('UTC');//Literaly useless but just in case } if($to->hour < 8) { $to->addDays(-1); $to->hour = 17; $to->minute = 0; $to->second = 0; } if($to->hour >= 17) { $to->hour = 17; $to->minute = 0; $to->second = 0; } if($from->hour >= 17) { $from->addDays(1); $from->hour = 8; $from->minute = 0; $from->second = 0; } if($from->hour < 8) { $from->hour = 8; $from->minute = 0; $from->second = 0; } if($from->gt($to)) return 0; $holidays = 0; //Future Scope $diffDays = $to->diffInDaysFiltered(function($date){ return !checkHoliday($date); }, $from)-1; $weekends = $to->diffInDaysFiltered(function($date){ return checkHoliday($date); }, $from);//Weekends or holidays $finalDiff = $diffDays * 9; $from->addDays($diffDays+$weekends); $diffHours = $to->diffInHoursFiltered(function($date) { if($date->hour > 8 && $date->hour < 17) return true; else return false; },$from)-1; $finalDiff += $diffHours; $from->addHours($diffHours); if($from->hour >= 17 || ($from->day < $to->day && !checkHoliday($to))) { $from->subHours($diffHours); $thatDay = clone $from; $thatDay->hour = 17; $thatDay->minute = 0; $thatDay->second=0; $prevMinutes = $from->diffInMinutes($thatDay); $toAddMinutes = ($diffHours * 60)-$prevMinutes; $from->addDays(1); while(checkHoliday($from)) $from->addDay(); $from->hour = 8; $from->minute = 0; $from->second = 0; $from->addMinutes($toAddMinutes); } $diffMinutes = $to->diffInMinutes($from); $finalDiff += $diffMinutes / 60; if($finalDiff<0) { $finalDiff = 0; return $finalDiff; } function checkHoliday($date) { //print_r($date); //dd(config('holidays.'.$date->year.'.'.$date->month)); if($date->isWeekend() || in_array($date->day,config('holidays.'.$date->year.'.'.$date->month))) return true; else return false; }
The structure of the config file.
<?php return [ '2016' => [ '1' => [1,18], //Month Based dates '2' => [15], '3' => [], '4' => [], '5' => [30], '6' => [], '7' => [4], '8' => [], '9' => [5], '10' => [], '11' => [11,24,25], '12' => [26], ],
Do remember that in PHP if you send the instances of Carbon as your arguments then you will not be able to use those instances again as their reference is sent. Use clone keyword while calling the function.
Hope that helped.
Update 4th January, 2017: Fixed a minor bug.
Hello.. i am currently working on a laravel project where i have to calculate business hours from 08:00 – 17:00 , then find their difference and dislpay it in hours .. like 4hrs or 3hrs … how can i tweak your code above to acieve the result..
No need to tweak the code just use it as a function. Send the from and to arguments as
getDiffHours(clone $from,clone $to);
Both arguments are Carbon dates;
There were some display issues due to a recent change in theme which made everything go out of place.
Let me know if you find any difficulties.
thank you. . i will get back to you if i face any difficulties..
How did it go?
Thank you it works great for me.
Glad to know it was helpful. Thanks for taking the time to comment.
function getWorkingHoursInSeconds(DateTime $start, DateTime $end, array $working_hours)
{
$seconds = 0; // Total working seconds
// Calculate the Start Date (Midnight) and Time (Seconds into day) as Integers.
$start_date = clone $start;
$start_date = $start_date->setTime(0, 0, 0)->getTimestamp();
$start_time = $start->getTimestamp() – $start_date;
// Calculate the Finish Date (Midnight) and Time (Seconds into day) as Integers.
$end_date = clone $end;
$end_date = $end_date->setTime(0, 0, 0)->getTimestamp();
$end_time = $end->getTimestamp() – $end_date;
// For each Day
for ($today = $start_date; $today <= $end_date; $today += 86400) {
// Get the current Weekday.
$today_weekday = date('w', $today);
// Skip to next day if no hours set for weekday.
if (!isset($working_hours[$today_weekday][0]) || !isset($working_hours[$today_weekday][1])) continue;
// Set the office hours start/finish.
$today_start = $working_hours[$today_weekday][0];
$today_end = $working_hours[$today_weekday][1];
// Adjust Start/Finish times on Start/Finish Day.
if ($today === $start_date) $today_start = min($today_end, max($today_start, $start_time));
if ($today === $end_date) $today_end = max($today_start, min($today_end, $end_time));
// Add to total seconds.
$seconds += $today_end – $today_start;
}
return gmdate("H:i:s", $seconds);
}
Thanks for sharing your code