计算2个日期时间之间的工作时间

时间:2008-09-26 19:15:35

标签: c# datetime date

给出两个日期时间。计算它们之间工作小时数的最佳方法是什么。考虑到工作时间是周一至周五8点到5点30分,以及周二至周五8点30分至5点30分,可能任何一天都可能是公众假期。

这是我的努力,看起来非常低效,但就迭代次数而言,IsWorkingDay方法命中数据库以查看该日期时间是否为公共假期。

任何人都可以建议任何优化或替代方案。

 public decimal ElapsedWorkingHours(DateTime start, DateTime finish)
        {

            decimal counter = 0;

            while (start.CompareTo(finish) <= 0)
            {   
                if (IsWorkingDay(start) && IsOfficeHours(start))
                {
                    start = start.AddMinutes(1);
                    counter++;
                }
                else
                {
                    start = start.AddMinutes(1);
                }
            }

            decimal hours;

            if (counter != 0)
            {
                hours = counter/60;
            }

            return hours;
        }

9 个答案:

答案 0 :(得分:3)

在开始优化之前,请问自己两个问题。

a)有效吗?

b)太慢了吗?

只有当两个问题的答案都是“是”时,您才准备开始优化。

除此之外

  • 您只需要担心开始日和结束日的分钟和小时数。干预日显然是整整9 / 9.5小时,除非他们是假期或周末
  • 无需查看周末,看看是否是假期

以下是我的做法

// Normalise start and end    
while start.day is weekend or holiday, start.day++, start.time = 0.00am
    if start.day is monday,
        start.time = max(start.time, 8am)
    else
        start.time = max(start.time, 8.30am)
while end.day is weekend or holiday, end.day--, end.time = 11.59pm
end.time = min(end.time, 5.30pm)

// Now we've normalised, is there any time left?    
if start > end
   return 0

// Calculate time in first day    
timediff = 5.30pm - start.time
day = start.day + 1
// Add time on all intervening days
while(day < end.day)
   // returns 9 or 9.30hrs or 0 as appropriate, could be optimised to grab all records
   // from the database in 1 or 2 hits, by counting all intervening mondays, and all
   // intervening tue-fris (non-holidays)
   timediff += duration(day) 

// Add time on last day
timediff += end.time - 08.30am
if end.day is Monday then
    timediff += end.time - 08.00am
else
    timediff += end.time - 08.30am

return timediff

你可以做点什么 每天从@Start和@End GROUP之间的假日中选择COUNT(天)

计算周一,周二,周三等假日的数量。可能是一种让SQL只计算星期一和非星期一的方法,但目前无法想到任何事情。

答案 1 :(得分:1)

  

特别是考虑到IsWorkingDay方法命中数据库以查看该日是否为公共假日

如果问题是查询的数量而不是数据的数量,请从数据库中查询开始时所​​需的整个日期范围的工作日数据,而不是在每次循环迭代中查询。

答案 2 :(得分:1)

看看TimeSpan Class。这将给你两次之间的时间。

单个数据库调用也可以在两次之间获得假期;类似的东西:

SELECT COUNT(*) FROM HOLIDAY WHERE HOLIDAY BETWEEN @Start AND @End

将该计数乘以8并将其从总小时数中减去。

-Ian

编辑:在下面的回复中,如果你的假期不是一个固定的小时数。您可以在数据库中保留HolidayStartHolidayEnd时间,并将其从调用返回到数据库。做一个小时的计数类似于你为主程序确定的任何方法。

答案 3 :(得分:1)

还有递归解决方案。不一定有效,但很有趣:

public decimal ElapseddWorkingHours(DateTime start, DateTime finish)
{
    if (start.Date == finish.Date)
        return (finish - start).TotalHours;

    if (IsWorkingDay(start.Date))
        return ElapsedWorkingHours(start, new DateTime(start.Year, start.Month, start.Day, 17, 30, 0))
            + ElapsedWorkingHours(start.Date.AddDays(1).AddHours(DateStartTime(start.Date.AddDays(1)), finish);
    else
        return ElapsedWorkingHours(start.Date.AddDays(1), finish);
}

答案 4 :(得分:0)

在@OregonGhost所说的基础上,不是在接受一天使用IsWorkingDay()函数并返回一个布尔值,而是使用一个接受范围的HolidayCount()函数,并返回一个整数,给出该范围内的Holidays数。这里的诀窍是,如果您正在处理边界开始和结束日期的部分日期,您可能仍需要确定这些日期是否为自己的假期。但即使这样,您也可以使用新方法确保最多需要 三次调用数据库。

答案 5 :(得分:0)

尝试以下几点:

TimeSpan = TimeSpan Between Date1 And Date2
cntDays = TimeSpan.Days 
cntNumberMondays = Iterate Between Date1 And Date2 Counting Mondays 
cntdays = cntdays - cntnumbermondays
NumHolidays = DBCall To Get # Holidays BETWEEN Date1 AND Date2
Cntdays = cntdays - numholidays 
numberhours = ((decimal)cntdays * NumberHoursInWorkingDay )+((decimal)cntNumberMondays * NumberHoursInMondayWorkDay )

答案 6 :(得分:0)

最有效的方法是计算总时差,然后减去周末或假日的时间。有很多边缘情况需要考虑,但您可以通过获取范围的第一天和最后几天并单独计算它们来简化它。

Ian Jacobs建议的COUNT(*)方法似乎是计算假期的好方法。无论你使用什么,它只会处理整天,你需要分别包括开始和结束日期。

计算周末的日子很容易;如果你有一个函数Weekday(date),它在星期一从0返回到星期日的6,它看起来像这样:

saturdays = ((finish - start) + Weekday(start) + 2) / 7;
sundays = ((finish - start) + Weekday(start) + 1) / 7;

注意:(完成 - 开始)不是字面意思,用计算时间跨度的东西替换它。

答案 7 :(得分:0)

使用@ Ian的查询检查日期,找出哪些天不是工作日。然后做一些数学计算,看看你的开始时间或结束时间是否属于非工作日并减去差异。

因此,如果开始时间是星期六中午,结束时间是星期一中午,则查询应该返回2天,从中计算48小时(2 x 24)。如果您对IsWorkingDay(开始)的查询返回false,则从开始到午夜的时间减去24,这将给您12小时或36小时的非工作时间。

现在,如果您的办公时间每天都相同,那么您也会做类似的事情。如果您的办公时间有点分散,那么您将遇到更多麻烦。

理想情况下,在数据库上进行一次查询,为您提供两次(甚至是日期)之间的所有办公时间。然后从该集合进行本地数学运算。

答案 8 :(得分:-1)

Dim totalMinutes As Integer = 0

For minute As Integer = 0 To DateDiff(DateInterval.Minute, contextInParameter1, contextInParameter2)
    Dim d As Date = contextInParameter1.AddMinutes(minute)
    If d.DayOfWeek <= DayOfWeek.Friday AndAlso _
       d.DayOfWeek >= DayOfWeek.Monday AndAlso _
       d.Hour >= 8 AndAlso _
       d.Hour <= 17 Then
        totalMinutes += 1
    Else
        Dim test = ""
    End If
Next minute

Dim totalHours = totalMinutes / 60

一块蛋糕!

干杯!