Calculate Business Days using LINQ

In this blog post, I will show one simple way to get the total business days within given range of two dates. Question related to this topic was asked several times in many ASP.NET / .NET Communities, so I thought it would be very good to document it with a blog post.

Since LINQ provides great ways to solve such requirements, in the following code I’m using LINQ’s functions.

First of all, I’ve created BusinessDays class (you can name it as you want) which contains two methods:

  • WorkingDay, returns true/false. True if date is not Saturday or Sunday.
  • GetAlldates, accepts two DateTime parameters where the first is the start date and the second is the end date of the Date range within we want to calculate the number of business days.
public static class BusinessDays
{
    public static bool WorkingDay(this DateTime date)
    {
        return date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday;
    }

    /*  this function is used only to collect the dates
        we are not paying much attention of the implementation */
    public static List<DateTime> GetAlldates(DateTime start, DateTime end)
    {
        List<DateTime> dates = new List<DateTime>();
        DateTime currLoopDate = start;            
        while (currLoopDate < end)
        {
            dates.Add(currLoopDate);
            currLoopDate = currLoopDate.AddDays(1);
        }
        return dates;
    }
}

Once we’ve created this class, here is how to implement it within your web application:

I’ve used the Button1_Click method which is triggered by the Button’s Click event. I only need to call two lines of code.

var dates = BusinessDays.GetAlldates(DateTime.Now, DateTime.Now.AddDays(7));
int getBizDays = dates.Where(day => day.WorkingDay()).Count();

#1 using BusinessDays.GetAlldates(DateTime.Now, DateTime.Now.AddDays(7)); I’m calling the GetAllDates static method from the BusinessDays static class, so that it will retrieve me a List of Dates within the given start-end range.

#2 in getBizDays I get the number of days calculated using the LINQ  functions Where and Count.

EDITED

RichardD suggested even better implementation of the GetAlldates method using IEnumerable

        public static IEnumerable<DateTime> GetAlldates(DateTime start, DateTime end)
        {
            DateTime currLoopDate = start;
            while (currLoopDate < end)
            {
                yield return currLoopDate;
                currLoopDate = currLoopDate.AddDays(1);
            }
        }

Also, we can shorten the dates.Where(day => day.WorkingDay()).Count() to only dates.Count(day => day.WorkingDay()).

Thanks RichardD

----

Hope this was useful post.

Regards,
Hajan

6 Comments

  • Brilliant! WIll be implementing this next week. Thanks.

  • DateTime currLoopDate = new DateTime();
    currLoopDate = start;

    You've just created a new DateTime instance and then immediately thrown it away. Just use: DateTime currLoopDate = start;

    Also, building a List to contain your results is pointless, since you're only using it to query the dates. Change the method to an iterator and you'll avoid the extra overhead.

    public static IEnumerable GetAlldates(DateTime start, DateTime end)
    {
    DateTime currLoopDate = start;
    while (currLoopDate day.WorkingDay());

  • Hi,

    #0 Thanks for your post)

    #1 More short solution:

    public IEnumerable&lt;DateTime&gt; RangeDateBy(DateTime startDay, DateTime endDay, Predicate&lt;DateTime&gt; condition)

    {

    // skipped params check ...

    var dayCount = (endDay - startDay).Days;

    return Enumerable.Range(0, dayCount)

    .Select(c =&gt; DateTime.Now.AddDays(c))

    .Where(condition.Invoke)

    .ToList();

    }

    Use:

    var workingDays = RangeDateBy(DateTime.Now, DateTime.Now.AddDays(7),

    d =&gt; (d.DayOfWeek != DayOfWeek.Saturday) &amp;&amp; (d.DayOfWeek != DayOfWeek.Sunday));

    #2 This solution doesn't take account of holidays and regional customs.

  • @osmirnov, thank you for your valuable feedback.
    Your solution seems very smart and definitely shortest.
    However, I have two things to say here:
    #1 I didn't pay attention at the GetAlldates method (as explained previously) because its only to have something on which I will check dates/count.
    #2 The way you've solved it is very clever, however, it might get confusing for LINQ newbies ;) - the point is 'How easy it is to calculate tihs using LINQ', the GetAlldates method is not important at all. I would really stick either to yours or to @RichardD's solution, but then this 'How easy it is...' will lose its meaning here ;).

    However, your comments are very valuable to me as well as to the Community.
    Thanks,
    Hajan

  • @osmirnov:

    1. Your Select statement is starting from the current date, not the start date passed in. It needs to be: .Select(c => startDay.AddDays(c))
    [Alternatively, in .NET 4 you can use: .Select(startDay.AddDays)]


    2. Since you're only returning an IEnumerable, you can skip the .ToList() call and avoid the extra overhead.


    3. You can avoid the "condition.Invoke" by changing your condition parameter to be a Func


    4. The purpose of LINQ is to construct queries from small reusable methods, rather than creating monolithic methods to cover every possibility. The Enumerable.Range method is a perfect example: it doesn't have an overload to return a filtered sequence, or an overload to return the sequence in reverse; it simply returns the sequence, and lets you use other LINQ operators to operate on it.

    Therefore, I think it makes more sense to leave the filtering predicate out of the date range method, and use the Where operator to filter it.

    In other words, I think:
    DateRange(start, end).Where(condition)
    is clearer and more flexible than:
    DateRange(start, end, condition)

  • Nice Work Hajan !! Really helpful
    I'm working on the same on my project currently, i'm also excluding Public Holidays too :) :) !!

Comments have been disabled for this content.