I’ve had to calculate working time between 2 dates as part of a dashboarding project.
As a result, I’ve built a function to do this for me.
First though I needed the public holidays to make sure that public holidays weren’t counted.
As an example, I’ve only used a couple of days, formatted like ’24-04′.
I’ve stored this in an external php file called hols.php
, however this can also be contained within the script.
Then for the main function. This takes 2 dateTime objects to compare. If the start is after the end, then it will return 0.
function work_minutes($dtStart,$dtEnd) {
if($dtStart > $dtEnd) {
return 0;
}
include('hols.php');
$di1Day = new DateInterval('P1D');
$workStartHour = 7; // When the work day starts
$workEndHour = 17; // When the work day ends
$workMinutes = 0; // Initialise the running counter that keeps tracks of the minutes of working time
$diffDays = $dtStart->diff($dtEnd)->format("%a"); // Get the number of days between the 2 timestamps, and add a day to ensure that all the days are checked.
for($x = 0; $x <= $diffDays+1; $x++) {
if($dtStart->format('N') < 6) { // checks that it's on a monday-friday
if(!in_array($dtStart->format('d-m'),$hols)) { // checks that it's not a public holiday
// Create a couple of new DateTime objects to define the start and end of
// the working day. These will be used to compare against when looping
// through each day to calculate the working minutes.
$dtStartOfDay = new DateTime($dtStart->format('Y-m-d').' '.$workStartHour.':00:00');
$dtEndOfDay = new DateTime($dtStart->format('Y-m-d').' '.$workEndHour.':00:00');
$signStart = '';
$signEnd = '';
// Compare the start of the day, if the starting DateTime Object is before the start of the working day, then the script will calculate the working time from
// the start of the day rather than the starting DateTime Object as any time before the start of the working day is irrelevant.
// The end DateTime objects are compared in a similar way. If the end DateTime object is before the end of the working day, that is used to compare, otherwise
// the end of the day is used to compare.
if($dtStartOfDay >= $dtStart) {
if($dtEndOfDay <= $dtEnd){
$arrDiff = explode(' ',$dtStartOfDay->diff($dtEndOfDay)->format('%H %i));
$signStart = $dtStartOfDay->diff($dtEndOfDay)->format('%R');
} else {
$arrDiff = explode(' ',$dtStartOfDay->diff($dtEnd)->format('%H %i));
$signStart = $dtStartOfDay->diff($dtEnd)->format('%R');
}
// When the starting DateTime object provided is after the start of the working day, then that will be used to calculate working minutes.
} else {
if($dtEndOfDay <= $dtEnd){
$arrDiff = explode(' ',$dtStart->diff($dtEndOfDay)->format('%H %i));
$signEnd = $dtStart->diff($dtEndOfDay)->format('%R');
} else {
$arrDiff = explode(' ',$dtStart->diff($dtEnd)->format('%H %i));
$signEnd = $dtStart->diff($dtEnd)->format('%R');
}
}
// intDiff contains the amount of minutes that was calculated as the amount of time between the start DateTime/Start of the day and end DateTime/End of the day.
$intDiff = $arrDiff[0]*60+$arrDiff[1];
// if theres any negative values, e.g. Starting DateTime stamp was after the end DateTime, the value is ignored, otherwise it adds to the running tally.
if($signStart != '-' && $signEnd != '-') {
$workMinutes += $intDiff;
}
}
}
// Add a day, and loop again.
$dtStart->add($di1Day);
$dtStart->setTime($workStartHour,0);
}
return $workMinutes;
}