计算C#中两个日期之间的工作日数

时间:2009-11-30 14:35:09

标签: c#

我如何获得两个给定日期之间的工作日数量,而不是迭代工作日之间的日期和计算工作日?

似乎相当简单,但我似乎找不到符合以下条件的确凿正确答案:

  1. 总数应该是包含的,所以GetNumberOfWeekdays(新的DateTime(2009,11,30),新的DateTime(2009,12,4))应该等于5,即周一到周五。
  2. 应该允许闰日
  3. 不只是在计算工作日期间迭代所有日期。
  4. 我发现similar question answer的{{3}}接近但不正确

11 个答案:

答案 0 :(得分:13)

O(1)解决方案:

// Count days from d0 to d1 inclusive, excluding weekends
public static int countWeekDays(DateTime d0, DateTime d1)
{
    int ndays = 1 + Convert.ToInt32((d1 - d0).TotalDays);
    int nsaturdays = (ndays + Convert.ToInt32(d0.DayOfWeek)) / 7;
    return ndays - 2 * nsaturdays
           - (d0.DayOfWeek == DayOfWeek.Sunday ? 1 : 0)
           + (d1.DayOfWeek == DayOfWeek.Saturday ? 1 : 0);
}

2014年1月的例子:

    January 2014
Su Mo Tu We Th Fr Sa
          1  2  3  4
 5  6  7  8  9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 1)); // 1
countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 2)); // 2
countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 3)); // 3
countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 4)); // 3
countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 5)); // 3
countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 6)); // 4

N.B。 DateTime输入应该在一天的大致同一时间。如果您仅根据上面的示例创建基于年,月和日的DateTime个对象,那么您应该没问题。作为一个反例,1月1日上午12点01分到1月2日晚上11:59仅跨越2天,但如果你使用这些时间,上述功能将计为3。

答案 1 :(得分:11)

从此link

    public static int Weekdays(DateTime dtmStart, DateTime dtmEnd)
    {
        // This function includes the start and end date in the count if they fall on a weekday
        int dowStart = ((int)dtmStart.DayOfWeek == 0 ? 7 : (int)dtmStart.DayOfWeek);
        int dowEnd = ((int)dtmEnd.DayOfWeek == 0 ? 7 : (int)dtmEnd.DayOfWeek);
        TimeSpan tSpan = dtmEnd - dtmStart;
        if (dowStart <= dowEnd)
        {
            return (((tSpan.Days / 7) * 5) + Math.Max((Math.Min((dowEnd + 1), 6) - dowStart), 0));
        }
        return (((tSpan.Days / 7) * 5) + Math.Min((dowEnd + 6) - Math.Min(dowStart, 6), 5));
    }


  [1]: http://www.eggheadcafe.com/community/aspnet/2/44982/how-to-calculate-num-of-w.aspx

测试(每个测试返回5):

    int ndays = Weekdays(new DateTime(2009, 11, 30), new DateTime(2009, 12, 4));
    System.Console.WriteLine(ndays);

    // leap year test
    ndays = Weekdays(new DateTime(2000, 2,27), new DateTime(2000, 3, 5));
    System.Console.WriteLine(ndays);

    // non leap year test
    ndays = Weekdays(new DateTime(2007, 2, 25), new DateTime(2007, 3, 4));
    System.Console.WriteLine(ndays);

答案 2 :(得分:3)

如果最后一天是周六或周日,那么eFloh的回答还有一天。这会解决它。

     public static int Weekdays(DateTime dtmStart, DateTime dtmEnd)
    {
        if (dtmStart > dtmEnd)
        {
            DateTime temp = dtmStart;
            dtmStart = dtmEnd;
            dtmEnd = temp;
        }

        /* Move border dates to the monday of the first full week and sunday of the last week */
        DateTime startMonday = dtmStart;
        int startDays = 1;
        while (startMonday.DayOfWeek != DayOfWeek.Monday)
        {
            if (startMonday.DayOfWeek != DayOfWeek.Saturday && startMonday.DayOfWeek != DayOfWeek.Sunday)
            {
                startDays++;
            }
            startMonday = startMonday.AddDays(1);
        }

        DateTime endSunday = dtmEnd;
        int endDays = 0;
        while (endSunday.DayOfWeek != DayOfWeek.Sunday)
        {
            if (endSunday.DayOfWeek != DayOfWeek.Saturday && endSunday.DayOfWeek != DayOfWeek.Sunday)
            {
                endDays++;
            }
            endSunday = endSunday.AddDays(1);
        }

        int weekDays;

        /* calculate weeks between full week border dates and fix the offset created by moving the border dates */
        weekDays = (Math.Max(0, (int)Math.Ceiling((endSunday - startMonday).TotalDays + 1)) / 7 * 5) + startDays - endDays;

        if (dtmEnd.DayOfWeek == DayOfWeek.Saturday || dtmEnd.DayOfWeek == DayOfWeek.Sunday)
        {
            weekDays -= 1;
        }

        return weekDays; 

    }

答案 3 :(得分:2)

这应该比dcp的解决方案做得更好:

    /// <summary>
    /// Count Weekdays between two dates
    /// </summary>
    /// <param name="dtmStart">first date</param>
    /// <param name="dtmEnd">second date</param>
    /// <returns>weekdays between the two dates, including the start and end day</returns>
    internal static int getWeekdaysBetween(DateTime dtmStart, DateTime dtmEnd)
    {
        if (dtmStart > dtmEnd)
        {
            DateTime temp = dtmStart;
            dtmStart = dtmEnd;
            dtmEnd = temp;
        }

        /* Move border dates to the monday of the first full week and sunday of the last week */
        DateTime startMonday = dtmStart;
        int startDays = 1;
        while (startMonday.DayOfWeek != DayOfWeek.Monday)
        {
            if (startMonday.DayOfWeek != DayOfWeek.Saturday && startMonday.DayOfWeek != DayOfWeek.Sunday)
            {
                startDays++;
            }
            startMonday = startMonday.AddDays(1);
        }

        DateTime endSunday = dtmEnd;
        int endDays = 0;
        while (endSunday.DayOfWeek != DayOfWeek.Sunday)
        {
            if (endSunday.DayOfWeek != DayOfWeek.Saturday && endSunday.DayOfWeek != DayOfWeek.Sunday)
            {
                endDays++;
            }
            endSunday = endSunday.AddDays(1);
        }

        int weekDays;

        /* calculate weeks between full week border dates and fix the offset created by moving the border dates */
        weekDays = (Math.Max(0, (int)Math.Ceiling((endSunday - startMonday).TotalDays + 1)) / 7 * 5) + startDays - endDays;

        return weekDays;
    }

答案 4 :(得分:2)

我需要正面/负面(不是绝对值)所以这就是我解决它的方法:

    public static int WeekdayDifference(DateTime StartDate, DateTime EndDate)
    {
        DateTime thisDate = StartDate;
        int weekDays = 0;
        while (thisDate != EndDate)
        {
            if (thisDate.DayOfWeek != DayOfWeek.Saturday && thisDate.DayOfWeek != DayOfWeek.Sunday) { weekDays++; }
            if (EndDate > StartDate) { thisDate = thisDate.AddDays(1); } else { thisDate = thisDate.AddDays(-1); }
        }

        /* Determine if value is positive or negative */
        if (EndDate > StartDate) {
            return weekDays;
        }
        else
        {
            return weekDays * -1;
        }
    }

答案 5 :(得分:2)

        public static int GetWeekDays(DateTime startDay, DateTime endDate, bool countEndDate = true)
        {
            var daysBetween = (int)(endDate - startDay).TotalDays;
            daysBetween = countEndDate ? daysBetween += 1 : daysBetween;
            return Enumerable.Range(0, daysBetween).Count(d => !startDay.AddDays(d).DayOfWeek.In(DayOfWeek.Saturday, DayOfWeek.Sunday));
        }

        public static bool In<T>(this T source, params T[] list)
        {
            if (null == source)
            {
                throw new ArgumentNullException("source");
            }
            return list.Contains(source);
        }

答案 6 :(得分:1)

  public static List<DateTime> Weekdays(DateTime startDate, DateTime endDate)
  {
      if (startDate > endDate)
      {
          Swap(ref startDate, ref endDate);
      }
      List<DateTime> days = new List<DateTime>();

      var ts = endDate - startDate;
      for (int i = 0; i < ts.TotalDays; i++)
      {
          var cur = startDate.AddDays(i);
          if (cur.DayOfWeek != DayOfWeek.Saturday && cur.DayOfWeek != DayOfWeek.Sunday)
              days.Add(cur);
          //if (startingDate.AddDays(i).DayOfWeek != DayOfWeek.Saturday || startingDate.AddDays(i).DayOfWeek != DayOfWeek.Sunday)
          //yield return startingDate.AddDays(i);
      }
      return days;
  }

交换日期

  private static void Swap(ref DateTime startDate, ref DateTime endDate)
  {
      object a = startDate;
      startDate = endDate;
      endDate = (DateTime)a;
  }

答案 7 :(得分:1)

实用程序用于获取一系列日期:

public IEnumerable<DateTime> GetDates(DateTime begin, int count)
{
    var first = new DateTime(begin.Year, begin.Month, begin.Day);
    var maxYield = Math.Abs(count);
    for (int i = 0; i < maxYield; i++)
    {
        if(count < 0)
            yield return first - TimeSpan.FromDays(i);
        else
            yield return first + TimeSpan.FromDays(i);      
    }
    yield break;
}

public IEnumerable<DateTime> GetDates(DateTime begin, DateTime end)
{
    var days = (int)Math.Ceiling((end - begin).TotalDays);
    return GetDates(begin, days);
}

LINQPad演示代码:

var begin = DateTime.Now;
var end = begin + TimeSpan.FromDays(14);

var dates = GetDates(begin, end);
var weekdays = dates.Count(x => x.DayOfWeek != DayOfWeek.Saturday && x.DayOfWeek != DayOfWeek.Sunday);
var mondays = dates.Count(x => x.DayOfWeek == DayOfWeek.Monday);
var firstThursday = dates
    .OrderBy(d => d)
    .FirstOrDefault(d => d.DayOfWeek == DayOfWeek.Thursday);

dates.Dump("Dates in Range");
weekdays.Dump("Count of Weekdays");
mondays.Dump("Count of Mondays");
firstThursday.Dump("First Thursday");

答案 8 :(得分:0)

此函数用于计算两个日期之间的DayOfWeek计数。小心地计算它(包括计算中的开始日和结束日):

private int GetWeekdayCount(DayOfWeek dayOfWeek, DateTime begin, DateTime end)
    {
        if (begin < end)
        {
            var timeSpan = end.Subtract(begin);
            var fullDays = timeSpan.Days;
            var count = fullDays / 7; // количество дней равно как минимум количеству полных недель попавших в диапазон
            var remain = fullDays % 7; // остаток от деления

            // и вычислим попал ли еще один день в те куски недели, что остаются от полной
            if (remain > 0)
            {
                var dowBegin = (int)begin.DayOfWeek;
                var dowEnd = (int)end.DayOfWeek;
                var dowDay = (int)dayOfWeek;
                if (dowBegin < dowEnd)
                {
                    // когда день недели начала меньше дня недели конца, например:
                    //  начало       конец
                    //    \/          \/
                    //    -- -- -- -- --
                    // Вс Пн Вт Ср Чт Пт Сб
                    if (dowDay >= dowBegin && dowDay <= dowEnd)
                        count++;
                }
                else
                {
                    // когда день недели начала больше дня недели конца, например:
                    //   конец      начало
                    //    \/          \/
                    // -- --          -- --
                    // Вс Пн Вт Ср Чт Пт Сб
                    if (dowDay <= dowEnd || dowDay >= dowBegin)
                        count++;
                }
            }
            else if (begin.DayOfWeek == dayOfWeek)
                count++;

            return count;
        }
        return 0;
    }

这是前一个功能的另一个简单模拟:

private int GetWeekdayCountStupid(DayOfWeek dayOfWeek, DateTime begin, DateTime end)
    {
        if (begin < end)
        {
            var count = 0;
            var day = begin;
            while (day <= end)
            {
                if (day.DayOfWeek == dayOfWeek)
                    count++;
                day = day.AddDays(1);
            }
            return count;
        }
        return 0;
    }

测试两种功能:

    [TestMethod()]
    public void TestWeekdayCount()
    {
        var init = new DateTime(2000, 01, 01);
        for (int day = 0; day < 7; day++)
        {
            var dayOfWeek = (DayOfWeek)day;
            for (int shift = 0; shift < 8; shift++)
            {
                for (int i = 0; i < 365; i++)
                {
                    var begin = init.AddDays(shift);
                    var end = init.AddDays(shift + i);
                    var count1 = GetWeekdayCount(dayOfWeek, begin, end);
                    var count2 = GetWeekdayCountStupid(dayOfWeek, begin, end);
                    Assert.AreEqual(count1, count2);
                }
            }
        }
    }

答案 9 :(得分:0)

var dates = new List<DateTime>();

        for (var dt = YourStartDate; dt <= YourEndDate; dt = dt.AddDays(1))
        {

            if (dt.DayOfWeek != DayOfWeek.Sunday && dt.DayOfWeek != DayOfWeek.Saturday)
            { dates.Add(dt); }

        }

在此代码中,您可以列出两个日期之间的所有商务日。

如果您想要计算这些日期,可以将dates.Count作为整数。 或者如果你想获得每一天,你可以将列表加入一个字符串。

答案 10 :(得分:0)

这是一个古老的问题,但我认为我会分享一个更灵活的答案,该答案可以删除一周中的任何一天。

我尝试通过最多循环6天来保持代码的清洁和易于阅读,同时保持效率。

因此对于OP,您可以像这样使用它:

    myDate.DaysUntill(DateTime.UtcNow, new List<DayOfWeek> { DayOfWeek.Saturday, DayOfWeek.Sunday });


    /// <summary>
    /// For better accuracy make sure <paramref name="startDate"/> and <paramref name="endDate"/> have the same time zone.
    /// This is only really useful if we use <paramref name="daysOfWeekToExclude"/> - otherwise the computation is trivial.
    /// </summary>
    /// <param name="startDate"></param>
    /// <param name="endDate"></param>
    /// <param name="daysOfWeekToExclude"></param>
    /// <returns></returns>
    public static int DaysUntill(this DateTime startDate, DateTime endDate, IEnumerable<DayOfWeek> daysOfWeekToExclude = null)
    {
        if (startDate >= endDate) return 0;
        daysOfWeekToExclude = daysOfWeekToExclude?.Distinct() ?? new List<DayOfWeek>();

        int nbOfWeeks = (endDate - startDate).Days / 7;
        int nbOfExtraDays = (endDate - startDate).Days % 7;

        int result = nbOfWeeks * (7 - daysOfWeekToExclude.Count());

        for (int i = 0; i < nbOfExtraDays; i++)
        {
            if (!daysOfWeekToExclude.Contains(startDate.AddDays(i + 1).DayOfWeek)) result++;
        }
        return result;
    }