Page 1 of 1

Calculate Business Hours between two dates

Posted: Thu Nov 17, 2005 8:18 am
by quadoc
Does anyone have a script or know where I can get a script to calculate business hours between two dates excluding holidays. I can probably create one myself but it would probably takes forever. :?

Posted: Thu Nov 17, 2005 9:37 am
by Weirdan
Once I had to calculate business days between two dates, here's what I came to:

Code: Select all

/**
     Number of seconds in a day
     **/
    define("DAYSECONDS", 60 * 60 * 24);
    
    /**
     counts holidays falling into the given range

     @param     $left   integer     leftmost end-point of the interval (unix timestamp)
     @param     $right  integer     rightmost end-point of the interval (unix timestamp)
     @return            integer     count of holidays falling into the given range
     **/
    function count_weekend_days($left, $right) {
        if($right < $left) return false;

        $firstmonday = strtotime("monday", $left);
        $lastmonday = strtotime("last monday", $right);

        $weeks_between = floor( ($lastmonday - $firstmonday) / (DAYSECONDS * 7) );

        $first_weekend = ceil( ($firstmonday - $left) / DAYSECONDS );
        $first_weekend = ($first_weekend < 2 ? $first_weekend : 2);

        $last_weekend = floor( ( strtotime("next monday", $lastmonday) - $right ) / DAYSECONDS );
        $last_weekend = ( (2 - $last_weekend) > 0 ? ( 2 - $last_weekend) : 0);

        return $first_weekend + 2 * $weeks_between + $last_weekend;

    }

    /**
     counts weekdays falling into the given range

     @param     $left   integer     leftmost end-point of the interval (unix timestamp)
     @param     $right  integer     rightmost end-point of the interval (unix timestamp)
     @return            integer     count of weekdays falling into the given range
     **/
    function count_working_days($left, $right) {
        if($right < $left) return false;
        return floor( ($right - $left) / DAYSECONDS ) - count_weekend_days($left, $right);
    }

Posted: Thu Nov 17, 2005 10:12 am
by pickle
Untested, but this might work:

Code: Select all

//set up holidays.  There may be a built in PHP function, but I don't know it.
$holidays[] = '24.12.2005';
$holidays[] = '25.12.2005';

$date1 = "1100674800";
$date2 = "1132243341";
$running_date = $date1;

$business_hours_per_day = 8;
$total_hours = 0;
do
{
   $day_number = date('w',$running_date);
   
   //only consider weekdays
   if($day_number != 0 && $day_number != 6)
   {
      //check if day is a holiday
      if(!in_array(date('j.m.Y',$running_date),$holidays)
      {
         $total_hours += $business_hours_per_day;
      }
   }
   $running_date = strototime('+1 day',$running_date);
}
while($running_date <= $date2);
This will also work when a day doesn't have 60 * 60 * 24 seconds (switching in and out of daylight saving time)

Posted: Thu Nov 17, 2005 10:43 am
by Weirdan
pickle wrote:This will also work when a day doesn't have 60 * 60 * 24 seconds (switching in and out of daylight saving time)
Mine works as well :) Rounding takes care of DST switching.

There's another pitfall in your algorithm: its execution time is in linear proportion to the difference between last_date and first_date, while mine has constant execution time.

Posted: Thu Nov 17, 2005 10:55 am
by pickle
Yes, but mine has more indentation, therefore is better in every possible way ;)

Though I do think mine's a little easier, conceptually, to understand - that point about execution time is a good one.

Posted: Thu Nov 17, 2005 11:17 am
by quadoc
Thanks for the tips guys, I tested both versions and here what I came up with.

Code: Select all

$date1 = 1131026473;
$date2 = 1131390020;
echo "begin date ".$date1." = ";echo date('m/d/Y g:i a D',$date1)."<br>";
echo "end date   ".$date2." = ";echo date('m/d/Y g:i a D',$date2)."<br>";
echo "Weirdan version <br>";
echo count_working_days($date1,$date2)." days";
echo "<br>";
echo "Pickle version<br>";
echo count_working_days2($date1,$date2)." hours";
begin date 1131026473 = 11/03/2005 9:01 am Thu
end date 1131390020 = 11/07/2005 2:00 pm Mon

Weirdan version
1 days

Pickle version
24 hours

I need it to calculate 9:01 - 6:00pm = 8hr on Thu
9:00 - 6:00pm = 8hr on Fri
9:00 - 2:00pm = 5hr on Mon
total = 21 hrs

Posted: Thu Nov 17, 2005 11:25 am
by pickle
Tack this on the end of mine:

Code: Select all

$running_total += (($running_date - $date2) * 60 * 60);

Again this is untested. What this does is add the difference (positive or negative) between the calculated end date and the actual end date.

Posted: Thu Nov 17, 2005 12:54 pm
by yum-jelly
I would do something like below, because then you can base exact hours on each day and also include holidays that may or may or may not be working holidays! You could also easily add different work weeks with different hours but that would be another example!

Code: Select all

<?

// the starting date

$start_date = '11-17-2005';

// the ending date

$end_date = '11-20-2005';

// the daily hours in a week! sunday to saturday

$work_hours = array ( 0, 8, 8, 8, 8, 8, 0 );

// an array of holidays (key = date, value = hours of work for that holiday)

$holidays = array ( '11-24-2005' => 4, '12-25-2005' => 0 );

echo hours_till ( $start_date, $end_date, $work_hours, $holidays );

function hours_till ( $s, $e, $w, $h )
{
	$r = array ();
	$s = mktime ( 0, 0, 0, substr ( $s, 0, 2 ), substr ( $s, 3, 2 ), substr ( $s, 6, 4 ) );
	$e = mktime ( 0, 0, 0, substr ( $e, 0, 2 ), substr ( $e, 3, 2 ), substr ( $e, 6, 4 ) );

	if ( is_array ( $h ) )
	{
		foreach ( $h AS $hn => $hv )
		{
			$temp = mktime ( 0, 0, 0, substr ( $hn, 0, 2 ), substr ( $hn, 3, 2 ), substr ( $hn, 6, 4 ) );

			if ( $temp >= $s && $temp <= $e )
			{
				$r[$temp] = $hv;
			}
		}
	}

	$th = 0;

	$ns = date ( 'w', $s );

	for ( $x = $s; $x <= $e; $x += 86400 )
	{
		$f = true;

		foreach ( $r AS $hn => $hv )
		{
			if ( $x == $hn )
			{
				$f = false;
				$th += $hv;
				unset ( $r[$hn] );
				break;
			}
		}

		if ( $f )
		{
			$th += $w[$ns];
		}

		$ns = ( $ns == 6 ? 0 : ++$ns );
	}

	return ( $th );
}

?>

yj

Posted: Fri Nov 18, 2005 8:02 am
by quadoc
yum-yelly, your version came up to 24hr based on the above inputs same as Pickle. How do I make it to calculate 21hr? Thanks for the tips though.

Posted: Fri May 19, 2006 2:10 pm
by quadoc
yum-yelly,

I was trying to understand the code and was wondering what is the variable $hv? Thanks...

Posted: Fri May 19, 2006 3:20 pm
by RobertGonzalez
It's the value for the array index 'hn' in the array 'h'. Wasn't it totally obvious, silly?

NOTE | This post is totally a joke. I just couldn't pass up a chance to poke fun at that variable naming convention. Carry on...