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.

  • Ifeanyi Elemson

    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..

    • Amarnath Maddireddy

      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.

      • Ifeanyi Elemson

        thank you. . i will get back to you if i face any difficulties..

        • Amarnath Maddireddy

          How did it go?