2009-10-24 195 views
68

在C#中,如何計算兩個日期之間的業務(或工作日)天數?計算兩個日期之間的營業日數?

+1

看看http://www.infopathdev.com/forums/t/7156.aspx – adatapost 2009-10-24 05:43:06

+4

所以你要排除的假期? – pxl 2009-10-24 07:46:27

+1

下面是C#中的一個例子,它也延伸到了獲得數小時的時間。 http://www.codeproject.com/KB/datetime/CalculatingBusinessHours.aspx – WesleyJohnson 2009-10-24 05:30:35

回答

98

我以前有過這樣的任務我已經有了解決方案。 我會避免枚舉所有可以避免的時間,這裏就是這種情況。我甚至沒有提到創建一堆DateTime實例,就像我在上面的答案中看到的那樣。這真是浪費處理能力。特別是在現實世界的情況下,當你必須檢查幾個月的時間間隔。 請參閱我的代碼,附帶評論,下面。

/// <summary> 
    /// Calculates number of business days, taking into account: 
    /// - weekends (Saturdays and Sundays) 
    /// - bank holidays in the middle of the week 
    /// </summary> 
    /// <param name="firstDay">First day in the time interval</param> 
    /// <param name="lastDay">Last day in the time interval</param> 
    /// <param name="bankHolidays">List of bank holidays excluding weekends</param> 
    /// <returns>Number of business days during the 'span'</returns> 
    public static int BusinessDaysUntil(this DateTime firstDay, DateTime lastDay, params DateTime[] bankHolidays) 
    { 
     firstDay = firstDay.Date; 
     lastDay = lastDay.Date; 
     if (firstDay > lastDay) 
      throw new ArgumentException("Incorrect last day " + lastDay); 

     TimeSpan span = lastDay - firstDay; 
     int businessDays = span.Days + 1; 
     int fullWeekCount = businessDays/7; 
     // find out if there are weekends during the time exceedng the full weeks 
     if (businessDays > fullWeekCount*7) 
     { 
      // we are here to find out if there is a 1-day or 2-days weekend 
      // in the time interval remaining after subtracting the complete weeks 
      int firstDayOfWeek = (int) firstDay.DayOfWeek; 
      int lastDayOfWeek = (int) lastDay.DayOfWeek; 
      if (lastDayOfWeek < firstDayOfWeek) 
       lastDayOfWeek += 7; 
      if (firstDayOfWeek <= 6) 
      { 
       if (lastDayOfWeek >= 7)// Both Saturday and Sunday are in the remaining time interval 
        businessDays -= 2; 
       else if (lastDayOfWeek >= 6)// Only Saturday is in the remaining time interval 
        businessDays -= 1; 
      } 
      else if (firstDayOfWeek <= 7 && lastDayOfWeek >= 7)// Only Sunday is in the remaining time interval 
       businessDays -= 1; 
     } 

     // subtract the weekends during the full weeks in the interval 
     businessDays -= fullWeekCount + fullWeekCount; 

     // subtract the number of bank holidays during the time interval 
     foreach (DateTime bankHoliday in bankHolidays) 
     { 
      DateTime bh = bankHoliday.Date; 
      if (firstDay <= bh && bh <= lastDay) 
       --businessDays; 
     } 

     return businessDays; 
    } 

編輯由Slauma,2011年8月

偉大的答案!雖然有小錯誤。我自由地編輯這個答案,因爲答覆者自2009年以來一直缺席。

上面的代碼假定DayOfWeek.Sunday的值爲7,但情況並非如此。該值實際上是0。如果例如firstDaylastDay週日都是相同的,則會導致錯誤的計算結果。在這種情況下該方法返回1,但它應該是0

此錯誤

最簡單的解決:在firstDayOfWeeklastDayOfWeek由以下聲明的行上面的代碼替換:

int firstDayOfWeek = firstDay.DayOfWeek == DayOfWeek.Sunday 
    ? 7 : (int)firstDay.DayOfWeek; 
int lastDayOfWeek = lastDay.DayOfWeek == DayOfWeek.Sunday 
    ? 7 : (int)lastDay.DayOfWeek; 

現在的結果是:

  • 週五至星期五 - > 1
  • 週六至週六 - > 0
  • 週日至週日 - > 0
  • 週五至週六 - > 1
  • 週五至週日 - > 1
  • 星期五至星期一 - > 2
  • 週六至週一 - > 1
  • 週日至週一 - > 1
  • 週一至週一 - > 1
+1

+1這可能是最簡單和最有效的方法(我的C++解決方案不使用TimeSpan的支持,C#使一些任務更容易)。銀行假日也是一個很好的接觸! – RedGlyph 2009-10-25 10:10:34

+2

還要確保銀行假期如下:if(firstDay <= bh && bh <= lastDay && bh.IsWorkingDay()) – Tawani 2011-10-10 21:21:05

+4

感謝您的方法。儘管如此,我必須在銀行假期減法/迭代if-statement:'&&!(bh.DayOfWeek == DayOfWeek.Sunday || bh.DayOfWeek == DayOfWeek.Saturday)中添加以下內容,否則它會減少相同的一天兩次,如果假期在週末。 – KristianB 2012-05-07 09:07:15

20

上的日期時間定義的擴展方法,像這樣:

public static class DateTimeExtensions 
{ 
    public static bool IsWorkingDay(this DateTime date) 
    { 
     return date.DayOfWeek != DayOfWeek.Saturday 
      && date.DayOfWeek != DayOfWeek.Sunday; 
    } 
} 

然後,使用不到Where子句來過濾日期更廣泛的名單:

var allDates = GetDates(); // method which returns a list of dates 

// filter dates by working day's 
var countOfWorkDays = allDates 
    .Where(day => day.IsWorkingDay()) 
    .Count() ; 
+0

你不只是繼續前進並延長時間跨度,所以你可以使用它 - 因爲他確實說過他想使用兩個日期之間的距離而不是日期列表? – WesleyJohnson 2009-10-24 09:20:05

+0

這兩個日期之間的距離是之間的天數,所以Count()就足夠了。 – 2009-10-24 10:23:31

+3

我不知道爲什麼這是一個合適的答案......他沒有單獨的日子列表,他有兩個日期,他想找到他們之間的工作日數。爲了使用這個解決方案,你必須提供*另一個*函數來產生twyp之間的每個日期的列表。 – 2009-10-24 18:18:40

4

下面是用於這一目的的一些代碼,與瑞典假期,但你可以適應什麼假期計數。請注意,我說你可能想要刪除的限制,但它是一個基於Web的系統和我沒有想任何人進入一些巨大的日期養豬過程

public static int GetWorkdays(DateTime from ,DateTime to) 
    { 
     int limit = 9999; 
     int counter = 0; 
     DateTime current = from; 
     int result = 0; 

     if (from > to) 
     { 
      DateTime temp = from; 
      from = to; 
      to = temp; 
     } 

     if (from >= to) 
     { 
      return 0; 
     } 


     while (current <= to && counter < limit) 
     { 
      if (IsSwedishWorkday(current)) 
      { 
       result++; 
      } 
      current = current.AddDays(1); 
      counter++; 

     } 
     return result; 
    } 


    public static bool IsSwedishWorkday(DateTime date) 
    { 
     return (!IsSwedishHoliday(date) && date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday); 
    } 

    public static bool IsSwedishHoliday(DateTime date) 
    { 
     return (
     IsSameDay(GetEpiphanyDay(date.Year), date) || 
     IsSameDay(GetMayDay(date.Year), date) || 
     IsSameDay(GetSwedishNationalDay(date.Year), date) || 
     IsSameDay(GetChristmasDay(date.Year), date) || 
     IsSameDay(GetBoxingDay(date.Year), date) || 
     IsSameDay(GetGoodFriday(date.Year), date) || 
     IsSameDay(GetAscensionDay(date.Year), date) || 
     IsSameDay(GetAllSaintsDay(date.Year), date) || 
     IsSameDay(GetMidsummersDay(date.Year), date) || 
     IsSameDay(GetPentecostDay(date.Year), date) || 
     IsSameDay(GetEasterMonday(date.Year), date) || 
     IsSameDay(GetNewYearsDay(date.Year), date) || 
     IsSameDay(GetEasterDay(date.Year), date) 
     ); 
    } 

    // Trettondagen 
    public static DateTime GetEpiphanyDay(int year) 
    { 
     return new DateTime(year, 1, 6); 
    } 

    // Första maj 
    public static DateTime GetMayDay(int year) 
    { 
     return new DateTime(year,5,1); 
    } 

    // Juldagen 
    public static DateTime GetSwedishNationalDay(int year) 
    { 
     return new DateTime(year, 6, 6); 
    } 


    // Juldagen 
    public static DateTime GetNewYearsDay(int year) 
    { 
     return new DateTime(year,1,1); 
    } 

    // Juldagen 
    public static DateTime GetChristmasDay(int year) 
    { 
     return new DateTime(year,12,25); 
    } 

    // Annandag jul 
    public static DateTime GetBoxingDay(int year) 
    { 
     return new DateTime(year, 12, 26); 
    } 


    // Långfredagen 
    public static DateTime GetGoodFriday(int year) 
    { 
     return GetEasterDay(year).AddDays(-3); 
    } 

    // Kristi himmelsfärdsdag 
    public static DateTime GetAscensionDay(int year) 
    { 
     return GetEasterDay(year).AddDays(5*7+4); 
    } 

    // Midsommar 
    public static DateTime GetAllSaintsDay(int year) 
    { 
     DateTime result = new DateTime(year,10,31); 
     while (result.DayOfWeek != DayOfWeek.Saturday) 
     { 
      result = result.AddDays(1); 
     } 
     return result; 
    } 

    // Midsommar 
    public static DateTime GetMidsummersDay(int year) 
    { 
     DateTime result = new DateTime(year, 6, 20); 
     while (result.DayOfWeek != DayOfWeek.Saturday) 
     { 
      result = result.AddDays(1); 
     } 
     return result; 
    } 

    // Pingstdagen 
    public static DateTime GetPentecostDay(int year) 
    { 
     return GetEasterDay(year).AddDays(7 * 7); 
    } 

    // Annandag påsk 
    public static DateTime GetEasterMonday(int year) 
    { 
     return GetEasterDay(year).AddDays(1); 
    } 
    public static DateTime GetEasterDay(int y) 
    { 
     double c; 
     double n; 
     double k; 
     double i; 
     double j; 
     double l; 
     double m; 
     double d; 
     c = System.Math.Floor(y/100.0); 
     n = y - 19 * System.Math.Floor(y/19.0); 
     k = System.Math.Floor((c - 17)/25.0); 
     i = c - System.Math.Floor(c/4) - System.Math.Floor((c - k)/3) + 19 * n + 15; 
     i = i - 30 * System.Math.Floor(i/30); 
     i = i - System.Math.Floor(i/28) * (1 - System.Math.Floor(i/28) * System.Math.Floor(29/(i + 1)) * System.Math.Floor((21 - n)/11)); 
     j = y + System.Math.Floor(y/4.0) + i + 2 - c + System.Math.Floor(c/4); 
     j = j - 7 * System.Math.Floor(j/7); 
     l = i - j; 
     m = 3 + System.Math.Floor((l + 40)/44);// month 
     d = l + 28 - 31 * System.Math.Floor(m/4);// day 

     double days = ((m == 3) ? d : d + 31); 

     DateTime result = new DateTime(y, 3, 1).AddDays(days-1); 

     return result; 
    } 
+0

功能issamedate缺失而只是公共靜態布爾IsSameDay(日期時間日期1,日期DATE2) \t \t { \t \t \t回date1.Date == date2.Date短路; \t \t} – 2014-12-04 14:03:10

68

好的。我認爲這是一次張貼了正確的答案:

public static double GetBusinessDays(DateTime startD, DateTime endD) 
{ 
    double calcBusinessDays = 
     1 + ((endD - startD).TotalDays * 5 - 
     (startD.DayOfWeek - endD.DayOfWeek) * 2)/7; 

    if (endD.DayOfWeek == DayOfWeek.Saturday) calcBusinessDays--; 
    if (startD.DayOfWeek == DayOfWeek.Sunday) calcBusinessDays--; 

    return calcBusinessDays; 
} 

原文出處:

http://alecpojidaev.wordpress.com/2009/10/29/work-days-calculation-with-c/

附:上面列出的解決方案讓我感到由於某種原因。

+9

好的工作,但也許使用DayOfWeek枚舉本身而不是將它們投入整數? – Neo 2012-06-27 12:56:01

+3

嚴重的是,最好的解決方案。乾杯亞歷克 – Mizmor 2012-11-08 23:48:29

+2

GetBusinessDays('3/18/2013','3/19/2013')== 2 ??? – xanadont 2013-03-18 14:30:08

29

我知道這個問題已經解決了,但我認爲我可以提供一個更直接的答案,可以幫助未來的其他訪問者。

這裏是我取它:

public int GetWorkingDays(DateTime from, DateTime to) 
{ 
    var dayDifference = (int)to.Subtract(from).TotalDays; 
    return Enumerable 
     .Range(1, dayDifference) 
     .Select(x => from.AddDays(x)) 
     .Count(x => x.DayOfWeek != DayOfWeek.Saturday && x.DayOfWeek != DayOfWeek.Sunday); 
} 

這是我最初提交:

public int GetWorkingDays(DateTime from, DateTime to) 
{ 
    var totalDays = 0; 
    for (var date = from; date < to; date = date.AddDays(1)) 
    { 
     if (date.DayOfWeek != DayOfWeek.Saturday 
      && date.DayOfWeek != DayOfWeek.Sunday) 
      totalDays++; 
    } 

    return totalDays; 
} 
+0

「哪裏」可以「算」來縮短它 – bygrace 2013-07-15 15:21:00

+0

@bygrace非常好的一點!謝謝,我編輯了答案。 – Alpha 2013-07-15 21:01:33

+0

更清晰,並列舉的解決方案適合消除銀行假期。儘管它們的散裝速度非常慢,在LINQPad中,計算100天迭代循環中90天差距的工作日需要10秒鐘,使用此解決方案需要10秒鐘,使用接受的答案或亞歷克Pojidaev更好。 – Whelkaholism 2014-10-07 14:20:22

1

我認爲上述答案都是正確的實際。他們都沒有解決所有的特殊情況,例如日期開始和結束於週末中間,日期開始於星期五,結束於下個星期一等等。最重要的是,它們將計算全部四捨五入到整體天,所以如果開始日期是在星期六的中間,例如,它會從工作日減去一整天,給出錯誤的結果...

無論如何,這是我的解決方案,相當有效和簡單並適用於所有情況。訣竅是隻是爲了找到以前的星期一開始和結束日期,然後做一個小的補償,在週末的時候開始和結束時會發生:

public double WorkDays(DateTime startDate, DateTime endDate){ 
     double weekendDays; 

     double days = endDate.Subtract(startDate).TotalDays; 

     if(days<0) return 0; 

     DateTime startMonday = startDate.AddDays(DayOfWeek.Monday - startDate.DayOfWeek).Date; 
     DateTime endMonday = endDate.AddDays(DayOfWeek.Monday - endDate.DayOfWeek).Date; 

     weekendDays = ((endMonday.Subtract(startMonday).TotalDays)/7) * 2; 

     // compute fractionary part of weekend days 
     double diffStart = startDate.Subtract(startMonday).TotalDays - 5; 
     double diffEnd = endDate.Subtract(endMonday).TotalDays - 5; 

     // compensate weekenddays 
     if(diffStart>0) weekendDays -= diffStart; 
     if(diffEnd>0) weekendDays += diffEnd; 

     return days - weekendDays; 
    } 
+2

如果使用星期六和星期日進行調用,則返回-1。 – Whelkaholism 2014-10-07 14:34:49

0

我只是分享我的解決辦法。它爲我工作,也許我只是不注意/知道這是一個錯誤。 我從第一個不完整的一週開始,如果有的話。 整個星期從星期日開始到星期六,所以如果(int)_now.DayOfWeek不是0(星期天),那麼第一週是不完整的。

我剛剛減去1到第一週星期六的第一週計數,然後將其添加到新計數;

然後我得到最後一個不完整的星期,然後減去1的星期日,然後加入新的計數。

然後,最後,將完整週數乘以5(平日)的數量加入到新計數中。

public int RemoveNonWorkingDays(int numberOfDays){ 

      int workingDays = 0; 

      int firstWeek = 7 - (int)_now.DayOfWeek; 

      if(firstWeek < 7){ 

       if(firstWeek > numberOfDays) 
        return numberOfDays; 

       workingDays += firstWeek-1; 
       numberOfDays -= firstWeek; 
      } 


      int lastWeek = numberOfDays % 7; 

      if(lastWeek > 0){ 

       numberOfDays -= lastWeek; 
       workingDays += lastWeek - 1; 

      } 

      workingDays += (numberOfDays/7)*5; 

      return workingDays; 
     } 
-1

這是一個通用的解決方案。

startdayvalue是開始日期的天數。

weekendday_1是週末的第幾天。

天數 - 星期一 - 星期二 - 星期二 - 星期六 - 星期六,星期日-7。

差是兩個日期之間差..

實施例:開始日期:2013年4月4日,結束日期:2013年4月14日

差異:10,startdayvalue:4,weekendday_1:7(如果SUNDAY是你的週末。)

這會給你假期的數量。

沒有工作日=(差別+ 1) - holiday1

if (startdayvalue > weekendday_1) 
    { 

     if (difference > ((7 - startdayvalue) + weekendday_1)) 
     { 
      holiday1 = (difference - ((7 - startdayvalue) + weekendday_1))/7; 
      holiday1 = holiday1 + 1; 
     } 
     else 
     { 
      holiday1 = 0; 
     } 
    } 
    else if (startdayvalue < weekendday_1) 
    { 

     if (difference > (weekendday_1 - startdayvalue)) 
     { 
      holiday1 = (difference - (weekendday_1 - startdayvalue))/7; 
      holiday1 = holiday1 + 1; 
     } 
     else if (difference == (weekendday_1 - startdayvalue)) 
     { 
      holiday1 = 1; 
     } 
     else 
     { 
      holiday1 = 0; 
     } 
    } 
    else 
    { 
     holiday1 = difference/7; 
     holiday1 = holiday1 + 1; 
    } 
8

我用下面的代碼也需要在考慮到節假日:

public class WorkingDays 
{ 
    public List<DateTime> GetHolidays() 
    { 
     var client = new WebClient(); 
     var json = client.DownloadString("https://www.gov.uk/bank-holidays.json"); 
     var js = new JavaScriptSerializer(); 
     var holidays = js.Deserialize <Dictionary<string, Holidays>>(json); 
     return holidays["england-and-wales"].events.Select(d => d.date).ToList(); 
    } 

    public int GetWorkingDays(DateTime from, DateTime to) 
    { 
     var totalDays = 0; 
     var holidays = GetHolidays(); 
     for (var date = from.AddDays(1); date <= to; date = date.AddDays(1)) 
     { 
      if (date.DayOfWeek != DayOfWeek.Saturday 
       && date.DayOfWeek != DayOfWeek.Sunday 
       && !holidays.Contains(date)) 
       totalDays++; 
     } 

     return totalDays; 
    } 
} 

public class Holidays 
{ 
    public string division { get; set; } 
    public List<Event> events { get; set; } 
} 

public class Event 
{ 
    public DateTime date { get; set; } 
    public string notes { get; set; } 
    public string title { get; set; } 
} 

和單元測試:

[TestClass] 
public class WorkingDays 
{ 
    [TestMethod] 
    public void SameDayIsZero() 
    { 
     var service = new WorkingDays(); 

     var from = new DateTime(2013, 8, 12); 

     Assert.AreEqual(0, service.GetWorkingDays(from, from)); 

    } 

    [TestMethod] 
    public void CalculateDaysInWorkingWeek() 
    { 
     var service = new WorkingDays(); 

     var from = new DateTime(2013, 8, 12); 
     var to = new DateTime(2013, 8, 16); 

     Assert.AreEqual(4, service.GetWorkingDays(from, to), "Mon - Fri = 4"); 

     Assert.AreEqual(1, service.GetWorkingDays(from, new DateTime(2013, 8, 13)), "Mon - Tues = 1"); 
    } 

    [TestMethod] 
    public void NotIncludeWeekends() 
    { 
     var service = new WorkingDays(); 

     var from = new DateTime(2013, 8, 9); 
     var to = new DateTime(2013, 8, 16); 

     Assert.AreEqual(5, service.GetWorkingDays(from, to), "Fri - Fri = 5"); 

     Assert.AreEqual(2, service.GetWorkingDays(from, new DateTime(2013, 8, 13)), "Fri - Tues = 2"); 
     Assert.AreEqual(1, service.GetWorkingDays(from, new DateTime(2013, 8, 12)), "Fri - Mon = 1"); 
    } 

    [TestMethod] 
    public void AccountForHolidays() 
    { 
     var service = new WorkingDays(); 

     var from = new DateTime(2013, 8, 23); 

     Assert.AreEqual(0, service.GetWorkingDays(from, new DateTime(2013, 8, 26)), "Fri - Mon = 0"); 

     Assert.AreEqual(1, service.GetWorkingDays(from, new DateTime(2013, 8, 27)), "Fri - Tues = 1"); 
    } 
} 
+3

+1 https://www.gov.uk/bank-holidays.json – dunxz 2014-07-08 10:05:08

+0

爲什麼你從「@」開始加1天來計算(var date = from.AddDays(1); date <= to ; date = date.AddDays(1))? – 2017-07-13 09:18:33

0

我在查找此代碼的固定TSQL版本時遇到了問題。以下實質上是C# code here的一個轉換,並添加了應該用於預先計算假期的假期表。

CREATE TABLE dbo.Holiday 
(
    HolidayDt  DATE NOT NULL, 
    Name   NVARCHAR(50) NOT NULL, 
    IsWeekday  BIT NOT NULL, 
    CONSTRAINT PK_Holiday PRIMARY KEY (HolidayDt) 
) 
GO 
CREATE INDEX IDX_Holiday ON Holiday (HolidayDt, IsWeekday) 

GO 

CREATE function dbo.GetBusinessDays 
(
    @FirstDay datetime, 
    @LastDay datetime 
) 
RETURNS INT 
AS 
BEGIN 
    DECLARE @BusinessDays INT, @FullWeekCount INT 
    SELECT @FirstDay = CONVERT(DATETIME,CONVERT(DATE,@FirstDay)) 
     , @LastDay = CONVERT(DATETIME,CONVERT(DATE,@LastDay)) 

    IF @FirstDay > @LastDay 
     RETURN NULL; 

    SELECT @BusinessDays = DATEDIFF(DAY, @FirstDay, @LastDay) + 1 
    SELECT @FullWeekCount = @BusinessDays/7; 

    -- find out if there are weekends during the time exceedng the full weeks 
    IF @BusinessDays > (@FullWeekCount * 7) 
    BEGIN 
    -- we are here to find out if there is a 1-day or 2-days weekend 
    -- in the time interval remaining after subtracting the complete weeks 
     DECLARE @firstDayOfWeek INT, @lastDayOfWeek INT; 
     SELECT @firstDayOfWeek = DATEPART(DW, @FirstDay), @lastDayOfWeek = DATEPART(DW, @LastDay); 

     IF @lastDayOfWeek < @firstDayOfWeek 
       SELECT @lastDayOfWeek = @lastDayOfWeek + 7; 

     IF @firstDayOfWeek <= 6 
      BEGIN 
       IF (@lastDayOfWeek >= 7) --Both Saturday and Sunday are in the remaining time interval 
        BEGIN 
         SELECT @BusinessDays = @BusinessDays - 2 
        END 
       ELSE IF @lastDayOfWeek>=6 --Only Saturday is in the remaining time interval 
        BEGIN 
         SELECT @BusinessDays = @BusinessDays - 1 
        END 

      END 
     ELSE IF @firstDayOfWeek <= 7 AND @lastDayOfWeek >=7 -- Only Sunday is in the remaining time interval 
     BEGIN 
      SELECT @BusinessDays = @BusinessDays - 1 
     END 
    END 

    -- subtract the weekends during the full weeks in the interval 
    DECLARE @Holidays INT; 
    SELECT @Holidays = COUNT(*) 
    FROM Holiday 
    WHERE HolidayDt BETWEEN @FirstDay AND @LastDay 
    AND  IsWeekday = CAST(1 AS BIT) 

    SELECT @BusinessDays = @BusinessDays - (@FullWeekCount + @FullWeekCount) -- - @Holidays 

    RETURN @BusinessDays 
END 
0
int BusinessDayDifference(DateTime Date1, DateTime Date2) 
    { 
     int Sign = 1; 
     if (Date2 > Date1) 
     { 
      Sign = -1; 
      DateTime TempDate = Date1; 
      Date1 = Date2; 
      Date2 = TempDate; 
     } 
     int BusDayDiff = (int)(Date1.Date - Date2.Date).TotalDays; 
     if (Date1.DayOfWeek == DayOfWeek.Saturday) 
      BusDayDiff -= 1; 
     if (Date2.DayOfWeek == DayOfWeek.Sunday) 
      BusDayDiff -= 1; 
     int Week1 = GetWeekNum(Date1); 
     int Week2 = GetWeekNum(Date2); 
     int WeekDiff = Week1 - Week2; 
     BusDayDiff -= WeekDiff * 2; 
     foreach (DateTime Holiday in Holidays) 
      if (Date1 >= Holiday && Date2 <= Holiday) 
       BusDayDiff--; 
     BusDayDiff *= Sign; 
     return BusDayDiff; 
    } 

    private int GetWeekNum(DateTime Date) 
    { 
     return (int)(Date.AddDays(-(int)Date.DayOfWeek).Ticks/TimeSpan.TicksPerDay/7); 
    } 
-1
public enum NonWorkingDays { SaturdaySunday = 0, FridaySaturday = 1 }; 
     public int getBusinessDates(DateTime dateSt, DateTime dateNd, NonWorkingDays nonWorkingDays = NonWorkingDays.SaturdaySunday) 
     { 
      List<DateTime> datelist = new List<DateTime>(); 
      while (dateSt.Date < dateNd.Date) 
      { 
       datelist.Add((dateSt = dateSt.AddDays(1))); 
      } 
      if (nonWorkingDays == NonWorkingDays.SaturdaySunday) 
      { 
       return datelist.Count(d => d.DayOfWeek != DayOfWeek.Saturday && 
         d.DayOfWeek != DayOfWeek.Friday); 
      } 
      else 
      { 
       return datelist.Count(d => d.DayOfWeek != DayOfWeek.Friday && 
         d.DayOfWeek != DayOfWeek.Saturday); 
      } 
     } 
0

以下是此問題的一個非常簡單的解決方案。我們有開始日期,結束日期和「for循環」,用於增加日期並通過轉換爲字符串DayOfWeek來計算以查看它是工作日還是週末。

class Program 
{ 
    static void Main(string[] args) 
    { 
     DateTime day = new DateTime(); 
     Console.Write("Inser your end date (example: 01/30/2015): "); 
     DateTime endDate = DateTime.Parse(Console.ReadLine()); 
     int numberOfDays = 0; 
     for (day = DateTime.Now.Date; day.Date < endDate.Date; day = day.Date.AddDays(1)) 
     { 
      string dayToString = Convert.ToString(day.DayOfWeek); 
      if (dayToString != "Saturday" && dayToString != "Sunday") numberOfDays++; 
     } 
     Console.WriteLine("Number of working days (not including local holidays) between two dates is "+numberOfDays); 
    } 
} 
0

基於該評論被列爲推薦的應答和補丁,以及 - >這個版本要在天轉換爲商務時間...慮當天小時爲好。

/// <summary> 
    /// Calculates number of business days, taking into account: 
    /// - weekends (Saturdays and Sundays) 
    /// - bank holidays in the middle of the week 
    /// </summary> 
    /// <param name="firstDay">First day in the time interval</param> 
    /// <param name="lastDay">Last day in the time interval</param> 
    /// <param name="bankHolidays">List of bank holidays excluding weekends</param> 
    /// <returns>Number of business hours during the 'span'</returns> 
    public static int BusinessHoursUntil(DateTime firstDay, DateTime lastDay, params DateTime[] bankHolidays) 
    { 
     var original_firstDay = firstDay; 
     var original_lastDay = lastDay; 
     firstDay = firstDay.Date; 
     lastDay = lastDay.Date; 
     if (firstDay > lastDay) 
      return -1; //// throw new ArgumentException("Incorrect last day " + lastDay); 

     TimeSpan span = lastDay - firstDay; 
     int businessDays = span.Days + 1; 
     int fullWeekCount = businessDays/7; 
     // find out if there are weekends during the time exceedng the full weeks 
     if (businessDays > fullWeekCount * 7) 
     { 
      // we are here to find out if there is a 1-day or 2-days weekend 
      // in the time interval remaining after subtracting the complete weeks 
      int firstDayOfWeek = firstDay.DayOfWeek == DayOfWeek.Sunday ? 7 : (int)firstDay.DayOfWeek; 
      int lastDayOfWeek = lastDay.DayOfWeek == DayOfWeek.Sunday ? 7 : (int)lastDay.DayOfWeek; 

      if (lastDayOfWeek < firstDayOfWeek) 
       lastDayOfWeek += 7; 
      if (firstDayOfWeek <= 6) 
      { 
       if (lastDayOfWeek >= 7)// Both Saturday and Sunday are in the remaining time interval 
        businessDays -= 2; 
       else if (lastDayOfWeek >= 6)// Only Saturday is in the remaining time interval 
        businessDays -= 1; 
      } 
      else if (firstDayOfWeek <= 7 && lastDayOfWeek >= 7)// Only Sunday is in the remaining time interval 
       businessDays -= 1; 
     } 

     // subtract the weekends during the full weeks in the interval 
     businessDays -= fullWeekCount + fullWeekCount; 

     if (bankHolidays != null && bankHolidays.Any()) 
     { 
      // subtract the number of bank holidays during the time interval 
      foreach (DateTime bankHoliday in bankHolidays) 
      { 
       DateTime bh = bankHoliday.Date; 
       if (firstDay <= bh && bh <= lastDay) 
        --businessDays; 
      } 
     } 

     int total_business_hours = 0; 
     if (firstDay.Date == lastDay.Date) 
     {//If on the same day, go granular with Hours from the Orginial_*Day values 
      total_business_hours = (int)(original_lastDay - original_firstDay).TotalHours; 
     } 
     else 
     {//Convert Business-Days to TotalHours 
      total_business_hours = (int)(firstDay.AddDays(businessDays).AddHours(firstDay.Hour) - firstDay).TotalHours; 
     } 
     return total_business_hours; 
    } 
0
using System; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      DateTime start = new DateTime(2014, 1, 1); 
      DateTime stop = new DateTime(2014, 12, 31); 

      int totalWorkingDays = GetNumberOfWorkingDays(start, stop); 

      Console.WriteLine("There are {0} working days.", totalWorkingDays); 
     } 

     private static int GetNumberOfWorkingDays(DateTime start, DateTime stop) 
     { 
      TimeSpan interval = stop - start; 

      int totalWeek = interval.Days/7; 
      int totalWorkingDays = 5 * totalWeek; 

      int remainingDays = interval.Days % 7; 


      for (int i = 0; i <= remainingDays; i++) 
      { 
       DayOfWeek test = (DayOfWeek)(((int)start.DayOfWeek + i) % 7); 
       if (test >= DayOfWeek.Monday && test <= DayOfWeek.Friday) 
        totalWorkingDays++; 
      } 

      return totalWorkingDays; 
     } 
    } 
} 
0

我只是改善@Alexander和@Slauma答案,以支持商業週刊作爲參數,對於週六是工作日的情況下,甚至情況下,有隻是一對夫婦的天周視爲營業日:

/// <summary> 
/// Calculate the number of business days between two dates, considering: 
/// - Days of the week that are not considered business days. 
/// - Holidays between these two dates. 
/// </summary> 
/// <param name="fDay">First day of the desired 'span'.</param> 
/// <param name="lDay">Last day of the desired 'span'.</param> 
/// <param name="BusinessDaysOfWeek">Days of the week that are considered to be business days, if NULL considers monday, tuesday, wednesday, thursday and friday as business days of the week.</param> 
/// <param name="Holidays">Holidays, if NULL, considers no holiday.</param> 
/// <returns>Number of business days during the 'span'</returns> 
public static int BusinessDaysUntil(this DateTime fDay, DateTime lDay, DayOfWeek[] BusinessDaysOfWeek = null, DateTime[] Holidays = null) 
{ 
    if (BusinessDaysOfWeek == null) 
     BusinessDaysOfWeek = new DayOfWeek[] { DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday }; 
    if (Holidays == null) 
     Holidays = new DateTime[] { }; 

    fDay = fDay.Date; 
    lDay = lDay.Date; 

    if (fDay > lDay) 
     throw new ArgumentException("Incorrect last day " + lDay); 

    int bDays = (lDay - fDay).Days + 1; 
    int fullWeekCount = bDays/7; 
    int fullWeekCountMult = 7 - WeekDays.Length; 
    // Find out if there are weekends during the time exceedng the full weeks 
    if (bDays > (fullWeekCount * 7)) 
    { 
     int fDayOfWeek = (int)fDay.DayOfWeek; 
     int lDayOfWeek = (int)lDay.DayOfWeek; 

     if (fDayOfWeek > lDayOfWeek) 
      lDayOfWeek += 7; 

     // If they are the same, we already covered it right before the Holiday subtraction 
     if (lDayOfWeek != fDayOfWeek) 
     { 
      // Here we need to see if any of the days between are considered business days 
      for (int i = fDayOfWeek; i <= lDayOfWeek; i++) 
       if (!WeekDays.Contains((DayOfWeek)(i > 6 ? i - 7 : i))) 
        bDays -= 1; 
     } 
    } 

    // Subtract the days that are not in WeekDays[] during the full weeks in the interval 
    bDays -= (fullWeekCount * fullWeekCountMult); 
    // Subtract the number of bank holidays during the time interval 
    bDays = bDays - Holidays.Select(x => x.Date).Count(x => fDay <= x && x <= lDay); 

    return bDays; 
} 
1

下面是一個快速示例代碼。這是一個類方法,所以只能在你的類中工作。如果您希望它是static,請將簽名更改爲private static(或public static)。

private IEnumerable<DateTime> GetWorkingDays(DateTime sd, DateTime ed) 
    { 
     for (var d = sd; d <= ed; d.AddDays(1)) 
      if (d.DayOfWeek != DayOfWeek.Saturday && d.DayOfWeek != DayOfWeek.Sunday) 
       yield return d; 
    } 

此方法創建一個循環變量d,其初始化爲開始日期,sd,則某一天每次迭代(d.AddDays(1))遞增。

它使用yield返回所需的值,該值創建iterator。關於迭代器的一件很酷的事情是,它們並沒有將IEnumerable的所有值保存在內存中,只是依次調用每一個值。這意味着您可以從時間的黎明到現在調用此方法,而不必擔心內存不足。

+1

此方法不返回兩個日期之間的營業日數,它返回兩個日期之間的營業日期。你提出的代碼非常乾淨,我喜歡yield的使用,但它不能回答這個問題。 – Martin 2015-05-29 16:54:24

0

以下是我們可以用來計算兩個日期之間的工作日的功能。我不使用假期名單,因爲它可能會因國家/地區而異。

如果我們無論如何要使用它,我們可以採取的第三個參數作爲節日的列表和遞增計數之前,我們應該檢查列表中不包含d

public static int GetBussinessDaysBetweenTwoDates(DateTime StartDate, DateTime EndDate) 
    { 
     if (StartDate > EndDate) 
      return -1; 

     int bd = 0; 

     for (DateTime d = StartDate; d < EndDate; d = d.AddDays(1)) 
     { 
      if (d.DayOfWeek != DayOfWeek.Saturday && d.DayOfWeek != DayOfWeek.Sunday) 
       bd++; 
     } 

     return bd; 
    } 
0

我認爲這可能是一個簡單的方法:

public int BusinessDaysUntil(DateTime start, DateTime end, params DateTime[] bankHolidays) 
    { 
     int tld = (int)((end - start).TotalDays) + 1; //including end day 
     int not_buss_day = 2 * (tld/7); //Saturday and Sunday 
     int rest = tld % 7; //rest. 

     if (rest > 0) 
     { 
      int tmp = (int)start.DayOfWeek - 1 + rest; 
      if (tmp == 6 || start.DayOfWeek == DayOfWeek.Sunday) not_buss_day++; else if (tmp > 6) not_buss_day += 2; 
     } 

     foreach (DateTime bankHoliday in bankHolidays) 
     { 
      DateTime bh = bankHoliday.Date; 
      if (!(bh.DayOfWeek == DayOfWeek.Saturday || bh.DayOfWeek == DayOfWeek.Sunday) && (start <= bh && bh <= end)) 
      { 
       not_buss_day++; 
      } 
     } 
     return tld - not_buss_day; 
    } 
3

這種解決方案避免了迭代中,既適用於+ ve和-ve平日的差異,並且包括一個單元測試套件,以針對計數平日的較慢方法迴歸。我還包括一個簡潔的方法來增加工作日,也可以用同樣的非迭代方式工作。

單元測試涵蓋了幾千個日期組合,以便詳盡地測試所有開始/結束週日組合與小型和大型日期範圍。

重要:我們假設我們通過排除開始日期和結束日期來計算天數。在計算平日時,這一點很重要,因爲您包含/排除的具體開始/結束日期會影響結果。這也確保了兩個相等日期之間的差異始終爲零,並且我們只包括完整的工作日,因爲通常您希望答案在當前開始日期(通常是今天)的任何時間都是正確的,並且包括完整的結束日期(例如到期日)。

注意:此代碼需要對假期進行額外調整,但與上述假設保持一致,此代碼必須排除開始日期的假期。

添加平日:

private static readonly int[,] _addOffset = 
{ 
    // 0 1 2 3 4 
    {0, 1, 2, 3, 4}, // Su 0 
    {0, 1, 2, 3, 4}, // M 1 
    {0, 1, 2, 3, 6}, // Tu 2 
    {0, 1, 4, 5, 6}, // W 3 
    {0, 1, 4, 5, 6}, // Th 4 
    {0, 3, 4, 5, 6}, // F 5 
    {0, 2, 3, 4, 5}, // Sa 6 
}; 

public static DateTime AddWeekdays(this DateTime date, int weekdays) 
{ 
    int extraDays = weekdays % 5; 
    int addDays = weekdays >= 0 
     ? (weekdays/5) * 7 + _addOffset[(int)date.DayOfWeek, extraDays] 
     : (weekdays/5) * 7 - _addOffset[6 - (int)date.DayOfWeek, -extraDays]; 
    return date.AddDays(addDays); 
} 

計算工作日的區別:

static readonly int[,] _diffOffset = 
{ 
    // Su M Tu W Th F Sa 
    {0, 1, 2, 3, 4, 5, 5}, // Su 
    {4, 0, 1, 2, 3, 4, 4}, // M 
    {3, 4, 0, 1, 2, 3, 3}, // Tu 
    {2, 3, 4, 0, 1, 2, 2}, // W 
    {1, 2, 3, 4, 0, 1, 1}, // Th 
    {0, 1, 2, 3, 4, 0, 0}, // F 
    {0, 1, 2, 3, 4, 5, 0}, // Sa 
}; 

public static int GetWeekdaysDiff(this DateTime dtStart, DateTime dtEnd) 
{ 
    int daysDiff = (int)(dtEnd - dtStart).TotalDays; 
    return daysDiff >= 0 
     ? 5 * (daysDiff/7) + _diffOffset[(int) dtStart.DayOfWeek, (int) dtEnd.DayOfWeek] 
     : 5 * (daysDiff/7) - _diffOffset[6 - (int) dtStart.DayOfWeek, 6 - (int) dtEnd.DayOfWeek]; 
} 

我發現,棧溢出大多數其他的解決方案要麼慢(迭代)或過於複雜,很多隻是簡單的不正確。道德故事是...不要相信它,除非你已經詳盡地測試了它

單元測試基於NUnit Combinatorial testingShouldBe NUnit擴展。

[TestFixture] 
public class DateTimeExtensionsTests 
{ 
    /// <summary> 
    /// Exclude start date, Include end date 
    /// </summary> 
    /// <param name="dtStart"></param> 
    /// <param name="dtEnd"></param> 
    /// <returns></returns> 
    private IEnumerable<DateTime> GetDateRange(DateTime dtStart, DateTime dtEnd) 
    { 
     Console.WriteLine(@"dtStart={0:yy-MMM-dd ddd}, dtEnd={1:yy-MMM-dd ddd}", dtStart, dtEnd); 

     TimeSpan diff = dtEnd - dtStart; 
     Console.WriteLine(diff); 

     if (dtStart <= dtEnd) 
     { 
      for (DateTime dt = dtStart.AddDays(1); dt <= dtEnd; dt = dt.AddDays(1)) 
      { 
       Console.WriteLine(@"dt={0:yy-MMM-dd ddd}", dt); 
       yield return dt; 
      } 
     } 
     else 
     { 
      for (DateTime dt = dtStart.AddDays(-1); dt >= dtEnd; dt = dt.AddDays(-1)) 
      { 
       Console.WriteLine(@"dt={0:yy-MMM-dd ddd}", dt); 
       yield return dt; 
      } 
     } 
    } 

    [Test, Combinatorial] 
    public void TestGetWeekdaysDiff(
     [Values(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 20, 30)] 
     int startDay, 
     [Values(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 20, 30)] 
     int endDay, 
     [Values(7)] 
     int startMonth, 
     [Values(7)] 
     int endMonth) 
    { 
     // Arrange 
     DateTime dtStart = new DateTime(2016, startMonth, startDay); 
     DateTime dtEnd = new DateTime(2016, endMonth, endDay); 

     int nDays = GetDateRange(dtStart, dtEnd) 
      .Count(dt => dt.DayOfWeek != DayOfWeek.Saturday && dt.DayOfWeek != DayOfWeek.Sunday); 

     if (dtEnd < dtStart) nDays = -nDays; 

     Console.WriteLine(@"countBusDays={0}", nDays); 

     // Act/Assert 
     dtStart.GetWeekdaysDiff(dtEnd).ShouldBe(nDays); 
    } 

    [Test, Combinatorial] 
    public void TestAddWeekdays(
     [Values(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 20, 30)] 
     int startDay, 
     [Values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 20, 30)] 
     int weekdays) 
    { 
     DateTime dtStart = new DateTime(2016, 7, startDay); 
     DateTime dtEnd1 = dtStart.AddWeekdays(weekdays);  // ADD 
     dtStart.GetWeekdaysDiff(dtEnd1).ShouldBe(weekdays); 

     DateTime dtEnd2 = dtStart.AddWeekdays(-weekdays); // SUBTRACT 
     dtStart.GetWeekdaysDiff(dtEnd2).ShouldBe(-weekdays); 
    } 
} 
+0

這個想法來自我在堆棧溢出中找到的SQL解決方案。他們的想法很紮實,但可惜它也有一個錯誤。它適用於+ ve值,但它們的查找表映射對於-ve值不正確。 – 2016-06-08 05:36:08

4

好吧,這已被打死了。 :)但是我仍然會提供另一個答案,因爲我需要一些不同的東西。該解決方案的不同之處在於它在開始和結束之間返回Business TimeSpan,您可以設置當天的營業時間並添加節假日。因此,您可以使用它來計算它是否在一天內,幾天內,週末甚至假期內發生。而且您可以僅通過營業日或僅通過從返回的TimeSpan對象獲取所需內容。以及它使用日期列表的方式,您可以看到添加非工作日的列表非常簡單,如果它不是典型的星期六和星期日。 我測試了一年,看起來超級快。

我只希望粘貼的代碼是準確的。但我知道它有效。

public static TimeSpan GetBusinessTimespanBetween(
    DateTime start, DateTime end, 
    TimeSpan workdayStartTime, TimeSpan workdayEndTime, 
    List<DateTime> holidays = null) 
{ 
    if (end < start) 
     throw new ArgumentException("start datetime must be before end datetime."); 

    // Just create an empty list for easier coding. 
    if (holidays == null) holidays = new List<DateTime>(); 

    if (holidays.Where(x => x.TimeOfDay.Ticks > 0).Any()) 
     throw new ArgumentException("holidays can not have a TimeOfDay, only the Date."); 

    var nonWorkDays = new List<DayOfWeek>() { DayOfWeek.Saturday, DayOfWeek.Sunday }; 

    var startTime = start.TimeOfDay; 

    // If the start time is before the starting hours, set it to the starting hour. 
    if (startTime < workdayStartTime) startTime = workdayStartTime; 

    var timeBeforeEndOfWorkDay = workdayEndTime - startTime; 

    // If it's after the end of the day, then this time lapse doesn't count. 
    if (timeBeforeEndOfWorkDay.TotalSeconds < 0) timeBeforeEndOfWorkDay = new TimeSpan(); 
    // If start is during a non work day, it doesn't count. 
    if (nonWorkDays.Contains(start.DayOfWeek)) timeBeforeEndOfWorkDay = new TimeSpan(); 
    else if (holidays.Contains(start.Date)) timeBeforeEndOfWorkDay = new TimeSpan(); 

    var endTime = end.TimeOfDay; 

    // If the end time is after the ending hours, set it to the ending hour. 
    if (endTime > workdayEndTime) endTime = workdayEndTime; 

    var timeAfterStartOfWorkDay = endTime - workdayStartTime; 

    // If it's before the start of the day, then this time lapse doesn't count. 
    if (timeAfterStartOfWorkDay.TotalSeconds < 0) timeAfterStartOfWorkDay = new TimeSpan(); 
    // If end is during a non work day, it doesn't count. 
    if (nonWorkDays.Contains(end.DayOfWeek)) timeAfterStartOfWorkDay = new TimeSpan(); 
    else if (holidays.Contains(end.Date)) timeAfterStartOfWorkDay = new TimeSpan(); 

    // Easy scenario if the times are during the day day. 
    if (start.Date.CompareTo(end.Date) == 0) 
    { 
     if (nonWorkDays.Contains(start.DayOfWeek)) return new TimeSpan(); 
     else if (holidays.Contains(start.Date)) return new TimeSpan(); 
     return endTime - startTime; 
    } 
    else 
    { 
     var timeBetween = end - start; 
     var daysBetween = (int)Math.Floor(timeBetween.TotalDays); 
     var dailyWorkSeconds = (int)Math.Floor((workdayEndTime - workdayStartTime).TotalSeconds); 

     var businessDaysBetween = 0; 

     // Now the fun begins with calculating the actual Business days. 
     if (daysBetween > 0) 
     { 
      var nextStartDay = start.AddDays(1).Date; 
      var dayBeforeEnd = end.AddDays(-1).Date; 
      for (DateTime d = nextStartDay; d <= dayBeforeEnd; d = d.AddDays(1)) 
      { 
       if (nonWorkDays.Contains(d.DayOfWeek)) continue; 
       else if (holidays.Contains(d.Date)) continue; 
       businessDaysBetween++; 
      } 
     } 

     var dailyWorkSecondsToAdd = dailyWorkSeconds * businessDaysBetween; 

     var output = timeBeforeEndOfWorkDay + timeAfterStartOfWorkDay; 
     output = output + new TimeSpan(0, 0, dailyWorkSecondsToAdd); 

     return output; 
    } 
} 

這裏是測試代碼:請注意,你就必須把這個函數在一個名爲DateHelper類的測試代碼工作。

[TestMethod] 
public void TestGetBusinessTimespanBetween() 
{ 
    var workdayStart = new TimeSpan(8, 0, 0); 
    var workdayEnd = new TimeSpan(17, 0, 0); 

    var holidays = new List<DateTime>() 
    { 
     new DateTime(2018, 1, 15), // a Monday 
     new DateTime(2018, 2, 15) // a Thursday 
    }; 

    var testdata = new[] 
    { 
     new 
     { 
      expectedMinutes = 0, 
      start = new DateTime(2016, 10, 19, 9, 50, 0), 
      end = new DateTime(2016, 10, 19, 9, 50, 0) 
     }, 
     new 
     { 
      expectedMinutes = 10, 
      start = new DateTime(2016, 10, 19, 9, 50, 0), 
      end = new DateTime(2016, 10, 19, 10, 0, 0) 
     }, 
     new 
     { 
      expectedMinutes = 5, 
      start = new DateTime(2016, 10, 19, 7, 50, 0), 
      end = new DateTime(2016, 10, 19, 8, 5, 0) 
     }, 
     new 
     { 
      expectedMinutes = 5, 
      start = new DateTime(2016, 10, 19, 16, 55, 0), 
      end = new DateTime(2016, 10, 19, 17, 5, 0) 
     }, 
     new 
     { 
      expectedMinutes = 15, 
      start = new DateTime(2016, 10, 19, 16, 50, 0), 
      end = new DateTime(2016, 10, 20, 8, 5, 0) 
     }, 
     new 
     { 
      expectedMinutes = 10, 
      start = new DateTime(2016, 10, 19, 16, 50, 0), 
      end = new DateTime(2016, 10, 20, 7, 55, 0) 
     }, 
     new 
     { 
      expectedMinutes = 5, 
      start = new DateTime(2016, 10, 19, 17, 10, 0), 
      end = new DateTime(2016, 10, 20, 8, 5, 0) 
     }, 
     new 
     { 
      expectedMinutes = 0, 
      start = new DateTime(2016, 10, 19, 17, 10, 0), 
      end = new DateTime(2016, 10, 20, 7, 5, 0) 
     }, 
     new 
     { 
      expectedMinutes = 545, 
      start = new DateTime(2016, 10, 19, 12, 10, 0), 
      end = new DateTime(2016, 10, 20, 12, 15, 0) 
     }, 
     // Spanning multiple weekdays 
     new 
     { 
      expectedMinutes = 835, 
      start = new DateTime(2016, 10, 19, 12, 10, 0), 
      end = new DateTime(2016, 10, 21, 8, 5, 0) 
     }, 
     // Spanning multiple weekdays 
     new 
     { 
      expectedMinutes = 1375, 
      start = new DateTime(2016, 10, 18, 12, 10, 0), 
      end = new DateTime(2016, 10, 21, 8, 5, 0) 
     }, 
     // Spanning from a Thursday to a Tuesday, 5 mins short of complete day. 
     new 
     { 
      expectedMinutes = 1615, 
      start = new DateTime(2016, 10, 20, 12, 10, 0), 
      end = new DateTime(2016, 10, 25, 12, 5, 0) 
     }, 
     // Spanning from a Thursday to a Tuesday, 5 mins beyond complete day. 
     new 
     { 
      expectedMinutes = 1625, 
      start = new DateTime(2016, 10, 20, 12, 10, 0), 
      end = new DateTime(2016, 10, 25, 12, 15, 0) 
     }, 
     // Spanning from a Friday to a Monday, 5 mins beyond complete day. 
     new 
     { 
      expectedMinutes = 545, 
      start = new DateTime(2016, 10, 21, 12, 10, 0), 
      end = new DateTime(2016, 10, 24, 12, 15, 0) 
     }, 
     // Spanning from a Friday to a Monday, 5 mins short complete day. 
     new 
     { 
      expectedMinutes = 535, 
      start = new DateTime(2016, 10, 21, 12, 10, 0), 
      end = new DateTime(2016, 10, 24, 12, 5, 0) 
     }, 
     // Spanning from a Saturday to a Monday, 5 mins short complete day. 
     new 
     { 
      expectedMinutes = 245, 
      start = new DateTime(2016, 10, 22, 12, 10, 0), 
      end = new DateTime(2016, 10, 24, 12, 5, 0) 
     }, 
     // Spanning from a Saturday to a Sunday, 5 mins beyond complete day. 
     new 
     { 
      expectedMinutes = 0, 
      start = new DateTime(2016, 10, 22, 12, 10, 0), 
      end = new DateTime(2016, 10, 23, 12, 15, 0) 
     }, 
     // Times within the same Saturday. 
     new 
     { 
      expectedMinutes = 0, 
      start = new DateTime(2016, 10, 22, 12, 10, 0), 
      end = new DateTime(2016, 10, 23, 12, 15, 0) 
     }, 
     // Spanning from a Saturday to the Sunday next week. 
     new 
     { 
      expectedMinutes = 2700, 
      start = new DateTime(2016, 10, 22, 12, 10, 0), 
      end = new DateTime(2016, 10, 30, 12, 15, 0) 
     }, 
     // Spanning a year. 
     new 
     { 
      expectedMinutes = 143355, 
      start = new DateTime(2016, 10, 22, 12, 10, 0), 
      end = new DateTime(2017, 10, 30, 12, 15, 0) 
     }, 
     // Spanning a year with 2 holidays. 
     new 
     { 
      expectedMinutes = 142815, 
      start = new DateTime(2017, 10, 22, 12, 10, 0), 
      end = new DateTime(2018, 10, 30, 12, 15, 0) 
     }, 
    }; 

    foreach (var item in testdata) 
    { 
     Assert.AreEqual(item.expectedMinutes, 
      DateHelper.GetBusinessTimespanBetween(
       item.start, item.end, 
       workdayStart, workdayEnd, 
       holidays) 
       .TotalMinutes); 
    } 
} 
0

我搜索了很多了,容易消化,算法計算工作日2日期間,同時也排除了國家法定節假日,最後我決定去使用這種方法:

public static int NumberOfWorkingDaysBetween2Dates(DateTime start,DateTime due,IEnumerable<DateTime> holidays) 
     { 
      var dic = new Dictionary<DateTime, DayOfWeek>(); 
      var totalDays = (due - start).Days; 
      for (int i = 0; i < totalDays + 1; i++) 
      { 
       if (!holidays.Any(x => x == start.AddDays(i))) 
        dic.Add(start.AddDays(i), start.AddDays(i).DayOfWeek); 
      } 

      return dic.Where(x => x.Value != DayOfWeek.Saturday && x.Value != DayOfWeek.Sunday).Count(); 
     } 

基本上,我想一起去每個日期和評價我的條件:

  1. 是不是星期六
  2. 是不是星期天
  3. 是不是國定假日

而且我想避免迭代日期。

通過運行和測量時間需要1到全年計算,我去了以下結果:

static void Main(string[] args) 
     { 
      var start = new DateTime(2017, 1, 1); 
      var due = new DateTime(2017, 12, 31); 

      var sw = Stopwatch.StartNew(); 
      var days = NumberOfWorkingDaysBetween2Dates(start, due,NationalHolidays()); 
      sw.Stop(); 

      Console.WriteLine($"Total working days = {days} --- time: {sw.Elapsed}"); 
      Console.ReadLine(); 

      // result is: 
      // Total working days = 249-- - time: 00:00:00.0269087 
     } 
0

這裏的另一個想法 - 這種方法允許指定任何工作周和節假日。

這裏的想法是,我們找到日期範圍從一週的第一個工作日到一週的最後一個週末日的核心。這使我們能夠輕鬆計算整個星期(,而不是遍歷所有日期)。我們所需要做的就是增加這個核心範圍開始和結束之前的工作日。

public static int CalculateWorkingDays(
    DateTime startDate, 
    DateTime endDate, 
    IList<DateTime> holidays, 
    DayOfWeek firstDayOfWeek, 
    DayOfWeek lastDayOfWeek) 
{ 
    // Make sure the defined working days run contiguously 
    if (lastDayOfWeek < firstDayOfWeek) 
    { 
     throw new Exception("Last day of week cannot fall before first day of week!"); 
    } 

    // Create a list of the days of the week that make-up the weekend by working back 
    // from the firstDayOfWeek and forward from lastDayOfWeek to get the start and end 
    // the weekend 
    var weekendStart = lastDayOfWeek == DayOfWeek.Saturday ? DayOfWeek.Sunday : lastDayOfWeek + 1; 
    var weekendEnd = firstDayOfWeek == DayOfWeek.Sunday ? DayOfWeek.Saturday : firstDayOfWeek - 1; 
    var weekendDays = new List<DayOfWeek>(); 

    var w = weekendStart; 
    do { 
     weekendDays.Add(w); 
     if (w == weekendEnd) break; 
     w = (w == DayOfWeek.Saturday) ? DayOfWeek.Sunday : w + 1; 
    } while (true); 


    // Force simple dates - no time 
    startDate = startDate.Date; 
    endDate = endDate.Date; 

    // Ensure a progessive date range 
    if (endDate < startDate) 
    { 
     var t = startDate; 
     startDate = endDate; 
     endDate = t; 
    } 

    // setup some working variables and constants 
    const int daysInWeek = 7;   // yeah - really! 
    var actualStartDate = startDate; // this will end up on startOfWeek boundary 
    var actualEndDate = endDate;  // this will end up on weekendEnd boundary 
    int workingDaysInWeek = daysInWeek - weekendDays.Count; 

    int workingDays = 0;  // the result we are trying to find 
    int leadingDays = 0;  // the number of working days leading up to the firstDayOfWeek boundary 
    int trailingDays = 0;  // the number of working days counting back to the weekendEnd boundary 

    // Calculate leading working days 
    // if we aren't on the firstDayOfWeek we need to step forward to the nearest 
    if (startDate.DayOfWeek != firstDayOfWeek) 
    { 
     var d = startDate; 
     do { 
      if (d.DayOfWeek == firstDayOfWeek || d >= endDate) 
      { 
       actualStartDate = d; 
       break; 
      } 
      if (!weekendDays.Contains(d.DayOfWeek)) 
      { 
       leadingDays++; 
      } 
      d = d.AddDays(1); 
     } while(true); 
    } 

    // Calculate trailing working days 
    // if we aren't on the weekendEnd we step back to the nearest 
    if (endDate >= actualStartDate && endDate.DayOfWeek != weekendEnd) 
    { 
     var d = endDate; 
     do { 
      if (d.DayOfWeek == weekendEnd || d < actualStartDate) 
      { 
       actualEndDate = d; 
       break; 
      } 
      if (!weekendDays.Contains(d.DayOfWeek)) 
      { 
       trailingDays++; 
      } 
      d = d.AddDays(-1); 
     } while(true); 
    } 

    // Calculate the inclusive number of days between the actualStartDate and the actualEndDate 
    var coreDays = (actualEndDate - actualStartDate).Days + 1; 
    var noWeeks = coreDays/daysInWeek; 

    // add together leading, core and trailing days 
    workingDays += noWeeks * workingDaysInWeek; 
    workingDays += leadingDays; 
    workingDays += trailingDays; 

    // Finally remove any holidays that fall within the range. 
    if (holidays != null) 
    { 
     workingDays -= holidays.Count(h => h >= startDate && (h <= endDate)); 
    } 

    return workingDays; 
} 
0

因爲我不能評論。接受的解決方案還有一個問題,即使在週末時,銀行假日也會被扣除。看看其他輸入是如何檢查的,這只是適合的。

在foreach因此應該是:

// subtract the number of bank holidays during the time interval 
    foreach (DateTime bankHoliday in bankHolidays) 
    { 
     DateTime bh = bankHoliday.Date; 

     // Do not subtract bank holidays when they fall in the weekend to avoid double subtraction 
     if (bh.DayOfWeek == DayOfWeek.Saturday || bh.DayOfWeek == DayOfWeek.Sunday) 
       continue; 

     if (firstDay <= bh && bh <= lastDay) 
      --businessDays; 
    } 
相關問題