65.9K
CodeProject 正在变化。 阅读更多。
Home

计算工作时间

starIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIconemptyStarIcon

1.33/5 (8投票s)

2007年7月11日

2分钟阅读

viewsIcon

66716

downloadIcon

1171

关于如何计算两个日期之间工作时间的文章

Screenshot - WorkTime.jpg

引言

我最近被问到如何计算两个给定日期之间的工作时长(小时/分钟)。我原本以为这不会太复杂,但实际上却不然。在这个示例应用程序中,我将使用硬编码的工作时间范围,从上午 8 点到下午 5 点来计算。目前尚未考虑节假日。

这两个日期之间有多少天?

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;
} 

好的,到目前为止一切顺利。现在我们已经有了工作天数,让我们来计算小时数。

日期范围内的第一天和最后一天是“特殊”的日子

我们可以计算每天 9 个小时(预定义的范围),但对于第一天和最后一天则不行。让我们开始计算第一天的秒数

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;                         
}

好的,现在用同样的方法处理最后一天

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;                   
}

现在全部加起来

准备好了。现在我们有了所有需要的时间值。如果我们把这些都放在一起,我们就可以得到一个带有三个参数的函数。

  • 参数 1:dtStart 是我们的开始日期和时间值,例如 2007/06/20 06:00:00 am
  • 参数 2:dtEnd 是我们的结束日期和时间值,例如 2007/07/14 05:00:00 pm
  • 参数 3:bIsUTCTime 当参数 1 和 2 是 UTC 时间值时,设置为 TRUE
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;
}

就这样!我希望它对您有所帮助。如果您发现任何错误或其他问题,请告诉我。祝您使用愉快!

实施说明

要在您的应用程序中实现此函数,请添加它并包含以下文件到您的项目中

  • RPCalcBusinessHours.cpp
  • RPCalcBusinessHour.h

该示例项目是在 VS6 下编译的,并在 Windows XP 上进行了测试。它不能在 Windows NT Workstation 3.5 之前的版本上运行。

用法

此软件以公共领域形式发布。您可以以任何您喜欢的方式自由使用它,但不得出售此源代码。如果您对其进行修改或扩展,请考虑在此处发布新代码供大家共享。此软件按“原样”提供,不提供任何明示或暗示的保证。我对因该软件造成的任何损害或业务损失不承担任何责任。

历史

  • 版本 1.0:2007/11/07 - 初始发布
© . All rights reserved.