Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

Calculating Business Hours

1.00/5 (13 votes)
11 Jul 20072 min read 2   1.1K  
An article on how to calculate business hours between two dates

Screenshot - WorkTime.jpg

Introduction

I was recently asked how many business hours/minutes are between two given dates. I thought the work of finding out would not be so heavy, but it was. In this sample application, I will calculate it with a hard-coded working day range from 8 am. to 5 pm. Holidays are not observed yet.

How many days are between these dates?

C++
int GetBusinessDays(CTime ctStart, CTime ctEnd)
{
   CTimeSpan ctp(ctEnd - ctStart);
   int iDays = ctp.GetDays() + 1;
   int iWeeks = iDays / 7;
   int iBusDays = iWeeks * 5;
   int iRem = iDays % 7;
   while (iRem > 0)
   { // no sunday, no saturday
      if ((ctStart.GetDayOfWeek() != 1) && (ctStart.GetDayOfWeek() != 7))
      {
         iBusDays++;
      }          ctStart += CTimeSpan(1,0,0,0); // add a day
      iRem--;
   }
   return iBusDays;
} 

Ok, so far so good. Now that we have the number of working days, let us calculate the hours.

The first and last day in the date range are "special" days

We can calculate each day with 9 hours per day (predefined range), but not the first and the last day. Let's start calculating seconds for the first day:

C++
DWORD CorrectFirstDayTime(CTime ctStart, CTime ctMaxTime, CTime ctMinTime)
{
    DWORD daysec = 0;
   
    if (ctMaxTime < ctStart) // start time is after max time
        return 0; // zero seconds for the first day
   
    if ((ctStart.GetDayOfWeek() == 1) || (ctStart.GetDayOfWeek() == 7))
        return 0;             // zero seconds      for weekend
   
    if (ctStart < ctMinTime) // start time is befor min time
        ctStart = ctMinTime; // set start time to min time
   
    // calulate seconds of this day
    CTimeSpan ctSpan(ctMaxTime - ctStart);
    daysec = 
        (ctSpan.GetDays() * 24 * 60 * 60) + (ctSpan.GetHours() * 60 * 60) +
        (ctSpan.GetMinutes() * 60) + ctSpan.GetSeconds();
   
    return daysec;                         
}

Ok, now the same with the last day:

C++
DWORD CorrectLastDayTime(CTime ctEnd, CTime ctMaxTime, CTime ctMinTime)
{
    DWORD daysec = 0;
   
    if (ctMinTime > ctEnd) // end time is befor min time
        return 0; // zero seconds for the end day
   
    if ((ctEnd.GetDayOfWeek() == 1) || (ctEnd.GetDayOfWeek() == 7))
        return 0;
   
    if (ctEnd > ctMaxTime) // end time is afer max time
        ctEnd = ctMaxTime;  // set end time to max time
   
    // calulate seconds of this day
    CTimeSpan ctSpan(ctEnd - ctMinTime);
    daysec = 
        (ctSpan.GetDays() * 24 *   60  * 60) + (ctSpan.GetHours() * 60 * 60) +
        (ctSpan.GetMinutes() * 60) + ctSpan.GetSeconds();
 
    return daysec;                   
}

All together now

Ready. Now we have all of the time values we need. If we put all this together, we get a function with three parameters.

  • Parameter 1: dtStart is our start date and time value, e.g. 06/20/2007 06:00:00 am
  • Parameter 2: dtEnd is our end date and time value, e.g. 07/14/2007 05:00:00 pm
  • Parameter 3: bIsUTCTime is set to TRUE when Parameters 1 and 2 are UTC time values
C++
double CalculateBusinessMinutes(CTime dtStart, CTime dtEnd, BOOL bIsUTCTime)
{
    // initialze our return value
    double OverAllMinutes = 0.0;
   
    // start time must be less than end time
    if (dtStart > dtEnd)
        return OverAllMinutes;
   
    if (bIsUTCTime)
    {
        // convert time from UTC to local time
        SYSTEMTIME ttStart;
        SYSTEMTIME ttEnd;
        SYSTEMTIME ttStartLocal;
        SYSTEMTIME ttEndLocal;
        dtStart.GetAsSystemTime(ttStart);
        dtEnd.GetAsSystemTime(ttEnd);
        SystemTimeToTzSpecificLocalTime(NULL, &ttStart, &ttStartLocal);
        SystemTimeToTzSpecificLocalTime(NULL, &ttEnd, &ttEndLocal);
        dtStart = ttStartLocal;
        dtEnd = ttEndLocal;
    }
   
    // initialize our temp times with midnight values
    CTime ctTempStart(dtStart.GetYear(), 
        dtStart.GetMonth(), dtStart.GetDay(), 0, 0, 0);
    CTime ctTempEnd(dtEnd.GetYear(), 
        dtEnd.GetMonth(), dtEnd.GetDay(), 0, 0, 0);
   
    // check if startdate and enddate are the same day
    BOOL bSameDay = (ctTempStart == ctTempEnd);
   
    // calculate the business days between the dates
    int iBusinessDays = GetBusinessDays(ctTempStart, ctTempEnd);
   
    // now add the time values to our temp times
    ctTempStart += CTimeSpan(0, dtStart.GetHour(), dtStart.GetMinute(), 0);
    ctTempEnd += CTimeSpan(0, dtEnd.GetHour(), dtEnd.GetMinute(), 0);
   
    // set our workingday time range and correct the first day
    CTime ctMaxTime(ctTempStart.GetYear(), 
        ctTempStart.GetMonth(), ctTempStart.GetDay(), 17, 0, 0);
    CTime ctMinTime(ctTempStart.GetYear(), 
         ctTempStart.GetMonth(), ctTempStart.GetDay(), 8, 0, 0);
    DWORD FirstDaySec = 
         CorrectFirstDayTime(ctTempStart, ctMaxTime, ctMinTime);


    // set our workingday time range and correct the last day

    CTime ctMaxTime1(ctTempEnd.GetYear(), 
        ctTempEnd.GetMonth(), ctTempEnd.GetDay(), 17, 0, 0);
    CTime ctMinTime1(ctTempEnd.GetYear(), 
        ctTempEnd.GetMonth(), ctTempEnd.GetDay(), 8, 0, 0);
    DWORD LastDaySec = CorrectLastDayTime(ctTempEnd, ctMaxTime1, ctMinTime1);
    DWORD OverAllSec = 0;
  
    // now sum-up all values
    if (bSameDay)
    { 
        if (iBusinessDays != 0)
        {
            CTimeSpan cts(ctMaxTime - ctMinTime);
            DWORD dwBusinessDaySeconds = 
                (cts.GetDays() * 24 * 60 * 60) + (cts.GetHours() * 60 * 60) + 
                (cts.GetMinutes() * 60) + cts.GetSeconds();
            OverAllSec = FirstDaySec + LastDaySec - dwBusinessDaySeconds;
        }
    }
    else
    {         
        if (iBusinessDays > 1)
            OverAllSec =  
            ((iBusinessDays - 2) * 9 * 60 * 60) + FirstDaySec + LastDaySec;
    }  
    OverAllMinutes = OverAllSec / 60;
  
    return OverAllMinutes;
}

That's it! I hope you'll find it useful. Please let me know about bugs and other problems if you find any. Enjoy!

Implementation notes

To implement this function into your application, add it and include the following files with your project:

  • RPCalcBusinessHours.cpp
  • RPCalcBusinessHour.h

The sample project was compiled under VS6 and has been tested on Windows XP. It will not work with versions earlier than Windows NT Workstation 3.5.

Usage

This software is released into the public domain. You are free to use it in any way you like, except that you may not sell this source code. If you modify it or extend it, please consider posting the new code here for everyone to share. This software is provided "as is" with no expressed or implied warranty. I accept no liability for any damage or loss of business that this software may cause.

History

  • Version 1.0: 07/11/2007 - Initial release

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here