Edit
I noticed some negative values being calculated lately. I'm still unsure why it's happening. Investigating and will post back when I have an answer.
I'm calculating the time it took a consultant to respond to a client using a start time and end time as input. I need to strip out any after hours, weekend, holidays etc and end up with only the time it took within business hours.
The basics of my function:
- Starting with the start date, I loop through it one day at a time until it reaches the same date as the end date.
- In my case 8.5 hours would be added for every day that passes in the loop.
- If there is a weekend/holiday in between the function just increments the date and does not add any additional seconds to the total time.
- If the loop reaches the end, it calculates the difference between the starting hour and end time and adds it to the total time.
Is this the most effective solution or can I improve?
I know there are a couple of answers and solutions out there, they did help me to get started but it wasn't inline with what I needed.
Below is my full code. The last function (businessHours) is the main function used for calculating the time.
This function just holds the basic settings like start & end time of a business day
function businessHours_settings()
{
// settings
$start = "08:00";
$end = "16:30";
// calculate amount of hours per working day
$diff = strtotime( $end ) - strtotime( $start );
$hours = $diff / 3600;
$settings = array(
"start" => $start,
"end" => $end,
"diff" => $diff,
"hrs" => $hours
);
return $settings;
}
This function returns a boolean whether the start time is within business hours
function _isWorkingHour( $time )
{
$settings = businessHours_settings();
if ( date("H", $time) >= date("H", strtotime( $settings['start'] )) && date("H", $time) <= date("H", strtotime( $settings['end'] )) )
{
return true;
}
else {
return false;
}
}
function _isPublicHoliday( $timestamp, $incName = false )
{
$ts = strtotime($timestamp);
$date = date("d-m-Y", $ts );
$mnth = date("m", $ts );
if ( $mnth == "12" )
{
$currYear = date("Y");
$nextYear = date("Y", strtotime( $currYear . " +365 days") );
}
elseif ( $mnth == "01" || $mnth == "1" )
{
$currYear = date("Y", strtotime( $currYear . " -365 days") );
$nextYear = date("Y");
}
This function returns all public holidays
// Public holidays of South Africa
$holidays = array(
"Human Rights Day" => "21-03-$currYear",
"Good Friday" => "30-03-$currYear",
"Family Day" => "02-04-$currYear",
"Freedom Day" => "27-04-$currYear",
"Labour Day" => "01-05-$currYear",
"Youth Day" => "16-06-$currYear",
"National Women's Day" => "09-08-$currYear",
"Heritage Day" => "24-09-$currYear",
"Day of Reconciliation" => "16-12-$currYear"
);
// Dates when company closes
$decemberHolidays = array(
"start" => strtotime("15-12-$currYear"),
"end" => strtotime("08-01-$nextYear")
);
if ( $ts > $decemberHolidays['start'] && $ts < $decemberHolidays['end'] )
{
return true;
}
$exists = array_search($date, $holidays);
if ( $exists !== false )
{
if ($incName)
{
return $exists;
}
else
{
return true;
}
}
else {
return false;
}
}
This is the main function that calculates the amount of secs between the start & end time.
function businessHours( $start, $end )
{
// settings
$settings = businessHours_settings();
$dayStart = $settings['start'];
$dayEnd = $settings['end'];
$diff = $settings['diff'];
$hourInDay = $settings['hrs'];
// timestamps
$start = strtotime($start);
$end = strtotime($end);
// if timestamps = false return original
if ( !$start || !$end ) return $end - $start;
$addDay = "";
$counter = $totalHours = $addTime = 0;
$secInDay = 60 * 60 * 24;
$dayDiff = ( $end - $start ) / $secInDay;
$dayDiffF = floor($dayDiff);
// check if start date is within working hours
$checkStart = _isWorkingHour($start);
if ( $checkStart && date("Y m d", $end) == date("Y m d", $start) )
{
return $end - $start;
}
else
{
if ( $checkStart === false )
{
// Move start time until 08:00
if ( date("G", $start) < 8 )
{
$start = strtotime( date("Y-m-d $dayStart", $start) );
}
elseif ( date("G", $start) <= 16 )
{
$start = strtotime( date("Y-m-d $dayStart", $start) . " +1 day" );
}
}
elseif ( $checkStart === true )
{
$addTime = $start - strtotime( date("Y-m-d $dayStart", $start) );
$start = strtotime( date("Y-m-d $dayStart", $start) );
}
$limit = date("j", $end);
$itrtr = date("j", $start);
while ( $itrtr < $limit )
{
if ( date("Y m d", $end) == date("Y m d", $start) ) break;
if (
date("N", strtotime( date("Y-m-d H:i:s", $start) . $addDay )) == "5" ||
date("N", strtotime( date("Y-m-d H:i:s", $start) . $addDay )) == "6" ||
_isPublicHoliday( date("N", strtotime( date("Y-m-d H:i:s", $start) . $addDay ))) ||
( date("N", strtotime( date("Y-m-d H:i:s", $start) . $addDay )) )
){
$itrtr++;
continue;
}
$totalHours += $diff;
$addDay = " +1 day";
$itrtr++;
}
$fEnd = strtotime( date("Y-m-d H:i", $end) );
$sEnd = strtotime( date("Y-m-d $dayStart", $end) );
$endTime = $fEnd - $sEnd;
$totalHours += $endTime;
$totalHours -= $addTime;
}
return $totalHours;
}
$start
and$end
times that you would pass to yourbusinessHours
function as well? \$\endgroup\$