【问题标题】:How to calculate count of business days between two dates?如何计算两个日期之间的工作日数?
【发布时间】:2013-02-13 02:08:37
【问题描述】:

我需要计算两个给定日期之间的工作日数(工作日)。工作日是一周中的所有日子,周六和周日除外。我没有考虑假期。

如何计算两个日期之间的工作日数?

【问题讨论】:

    标签: delphi delphi-xe2


    【解决方案1】:
    function BusinessDaysSinceFixedDate ( const nDate : tDateTime ) : integer;
    const
      Map : array [ -6 .. 6 ] of integer
          = (  0, 0, 1, 2, 3, 4, 5, 5, 5, 6, 7, 8, 9 ); 
    var
      X : integer;
    begin
      X := trunc ( nDate );
      Result := 5 * ( X div 7 ) + Map [ X mod 7 ];
    end;
    
    function BusinessDaysBetweenDates ( const nStartDate : tDateTime;
                                        const nEndDate   : tDateTime ) : integer;
    begin
      Result :=   BusinessDaysSinceFixedDate ( nEndDate )
                - BusinessDaysSinceFixedDate ( nStartDate );
    end;
    

    例程 BusinessDaysSinceFixedDate 计算自固定日期以来的工作日数。不相关的具体日期是 1899 年 12 月 25 日星期一。 它只是计算已经过去的周数(X div 7)并将其乘以 5。 然后它会根据星期几添加一个偏移量以进行更正。 请注意,(X mod 7) 将为负日期返回负值,即 1899 年 12 月 30 日之前的日期。

    例程 BusinessDaysBetweenDates 只需调用 BusinessDaysSinceFixedDate 来获取开始日期和结束日期,然后从另一个中减去一个。

    【讨论】:

    • +1。您可以将 Abs 添加到 BusinessDaysBetweenDates 的结果中(因此 Days 的结果将始终为正)。
    【解决方案2】:

    不循环所有天并且输入参数不依赖于顺序。

    Uses DateUtils,Math;
    
    function WorkingDaysBetween( const firstDate,secondDate : TDateTime) : Integer;
    var
      startDate,stopDate : TDateTime;
      startDow,stopDow : Integer;
    begin
      if (firstDate < secondDate) then
      begin
        startDate := firstDate;
        stopDate := secondDate;
      end
      else
      begin
        startDate := secondDate;
        stopDate := firstDate;
      end; 
      startDow := DayOfTheWeek(startDate);
      stopDow := DayOfTheWeek(stopDate);
      if (stopDow >= startDow) then
        stopDow := Min(stopDow,6)
      else
        Inc(stopDow,5); 
    
      Result := 
        5*WeeksBetween(stopDate,startDate) + 
        (stopDow - Min(startDow,6));
    end;
    

    【讨论】:

    • +1。好的!我没有机会查看非循环解决方案 - 现在我绝对不需要这样做。 :-)
    • 如果我测试dt1 := Nowdt2 := IncYear(Now, 3),我会得到与其他两个不同的结果,使用你的函数。
    • @kobik,谢谢。正确的日期编号函数当然是DayOfTheWeek()
    【解决方案3】:

    您需要使用DayOfTheWeek(来自DateUtils 单元)和一个计数器,从开始日期迭代到结束日期。 (您可能还需要一张假期表,以将其排除在您的计数之外。)

    function BusinessDaysBetween(const StartDate, EndDate: TDateTime): Integer;
    var
      CurrDate : TDateTime;
    begin
      CurrDate := StartDate;
      Result := 0;
      while (CurrDate <= EndDate) do
      begin
        // DayOfTheWeek returns 1-5 for Mon-Fri, so 6 and 7 are weekends
        if DayOfTheWeek(CurrDate) < 6 then
          Inc(Result);
        CurrDate := CurrDate + 1;
      end;
    end;
    

    您可以通过不担心参数的顺序来稍微增强这一点(换句话说,无论 start 在 end 之前还是 end 在 start 之前,函数仍然可以工作):

    function BusinessDaysBetween(const FirstDate, SecondDate: TDateTime): Integer;
    var
      CurrDate : TDateTime;
      StartDate, EndDate: TDateTime;
    begin
      if SecondDate > FirstDate then
      begin
        StartDate := FirstDate;
        EndDate := SecondDate;
      end
      else
      begin
        StartDate := SecondDate;
        EndDate := FirstDate;
      end;
    
      CurrDate := StartDate;
      Result := 0;
    
      while (CurrDate <= EndDate) do
      begin
        if DayOfTheWeek(CurrDate) < 6 then
          Inc(Result);
        CurrDate := CurrDate + 1;
      end;
    end;
    

    【讨论】:

    • 我明白了,谢谢...我不需要假期,因为它们不会显着影响周转时间..但是周末是个问题。我会试一试。
    • 我使用了几乎相同的功能..效果很好。也有很多标本,所以如果很少有假期有问题,它们不会影响整体......谢谢。
    • 如果没有循环就好了。
    • @DavidHeffernan:+1。我把它列在我的待办事项清单上只是为了好玩,但 LU RD 和 user1008646 打败了我。 :)
    • Sardukar:您应该将接受的答案更改为@LURD 或 user1008648 发布的答案。两者都比我的解决方案快得多。 (user10008646 的速度稍快,但 IMO LURD 更容易在以后阅读和维护。)
    猜你喜欢
    • 1970-01-01
    • 2018-08-03
    • 2010-09-20
    • 1970-01-01
    • 1970-01-01
    • 2014-10-20
    • 1970-01-01
    相关资源
    最近更新 更多