Calculate the number of weekdays between two dates in C#

前端 未结 11 2231
温柔的废话
温柔的废话 2020-12-03 16:59

How can I get the number of weekdays between two given dates without just iterating through the dates between and counting the weekdays?

Seems fairly straightforward

相关标签:
11条回答
  • 2020-12-03 17:38

    Here the function, that calculates count of DayOfWeek betwean two dates. Be carefully it calculates it inclusively (includes beginning day and ending day in calculation):

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

    Here is another one simple analog of previous function:

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

    And tests for both functions:

        [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);
                    }
                }
            }
        }
    
    0 讨论(0)
  • 2020-12-03 17:40

    Utility functions to get a range of dates:

    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 demo code:

    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");
    
    0 讨论(0)
  • 2020-12-03 17:43
            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);
            }
    
    0 讨论(0)
  • 2020-12-03 17:45

    This is an old question but I figured I would share a more flexible answer which allows to removes any day of the week.

    I tried to keep the code clean and easy to read while remaining efficient by only looping through 6 days max.

    So for the OP you can use it like this:

        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;
        }
    
    0 讨论(0)
  • 2020-12-03 17:48

    eFloh's answer had an extra day if the last day was a Saturday or Sunday. This would fix it.

         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; 
    
        }
    
    0 讨论(0)
提交回复
热议问题