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?
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)
{ if ((ctStart.GetDayOfWeek() != 1) && (ctStart.GetDayOfWeek() != 7))
{
iBusDays++;
} ctStart += CTimeSpan(1,0,0,0); 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:
DWORD CorrectFirstDayTime(CTime ctStart, CTime ctMaxTime, CTime ctMinTime)
{
DWORD daysec = 0;
if (ctMaxTime < ctStart) return 0;
if ((ctStart.GetDayOfWeek() == 1) || (ctStart.GetDayOfWeek() == 7))
return 0;
if (ctStart < ctMinTime) ctStart = ctMinTime;
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:
DWORD CorrectLastDayTime(CTime ctEnd, CTime ctMaxTime, CTime ctMinTime)
{
DWORD daysec = 0;
if (ctMinTime > ctEnd) return 0;
if ((ctEnd.GetDayOfWeek() == 1) || (ctEnd.GetDayOfWeek() == 7))
return 0;
if (ctEnd > ctMaxTime) ctEnd = ctMaxTime;
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
double CalculateBusinessMinutes(CTime dtStart, CTime dtEnd, BOOL bIsUTCTime)
{
double OverAllMinutes = 0.0;
if (dtStart > dtEnd)
return OverAllMinutes;
if (bIsUTCTime)
{
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;
}
CTime ctTempStart(dtStart.GetYear(),
dtStart.GetMonth(), dtStart.GetDay(), 0, 0, 0);
CTime ctTempEnd(dtEnd.GetYear(),
dtEnd.GetMonth(), dtEnd.GetDay(), 0, 0, 0);
BOOL bSameDay = (ctTempStart == ctTempEnd);
int iBusinessDays = GetBusinessDays(ctTempStart, ctTempEnd);
ctTempStart += CTimeSpan(0, dtStart.GetHour(), dtStart.GetMinute(), 0);
ctTempEnd += CTimeSpan(0, dtEnd.GetHour(), dtEnd.GetMinute(), 0);
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);
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;
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