0

i need to calculate the number of workdays between two dates. a workday is any day between Monday through Friday except for holidays. the code below does this, but it uses a loop. does anyone see a way to get rid of the loop or at least optimize it?

thanks konstantin


using System;
using System.Linq;

namespace consapp
{
    static class Program
    {
        static void Main(string[] args)
        {
            var holidays = new DateTime[] { new DateTime(2010, 11, 23), new DateTime(2010, 11, 30) };

            var date_start = new DateTime(2010, 12, 3);
            var date_end = date_start.AddDays(-9.9);
            var duration = (date_end - date_start).Duration();

            for (var d = date_end; d < date_start; d = d.Date.AddDays(1))
            {
                if (holidays.Contains(d.Date) || d.DayOfWeek == DayOfWeek.Saturday || d.DayOfWeek == DayOfWeek.Sunday)
                {
                    duration -= TimeSpan.FromDays(1) - d.TimeOfDay;
                }
            }
            Console.WriteLine(duration);
        }
    }
}
akonsu
  • 28,824
  • 33
  • 119
  • 194
  • 5
    Have you first tested if it works correctly before trying to optimize it? Have you profiled the code to make sure that this is the bottleneck? – Mark Byers Dec 06 '10 at 19:32
  • What date ranges does your program generally run? How well does the code perform now and how well do you need it to perform? While I'm sure there are more optimal ways of doing this, I would first confirm that it's performing poorly. – John Bledsoe Dec 06 '10 at 19:32
  • possible duplicate of [Calculate the number of business days between two dates?](http://stackoverflow.com/questions/1617049/calculate-the-number-of-business-days-between-two-dates) – NotMe Dec 06 '10 at 19:32
  • i am writing a chart which needs to display financial market data for an arbitrary period. since the user needs to be able to scroll the chart, and since i have to find the date at the left edge of the chart in order to display the data, this code should be very fast. – akonsu Dec 06 '10 at 19:45

5 Answers5

0

Is performance really problematic here? Unless profiling suggests otherwise I'd guess this code doesn't really slow down your application. It's performance should be fine unless you calculate the workdays for thousands of long intervals per second.

If your holiday list is much larger than just two dates then convert it into a HashSet<T> which has O(1) lookup time.

And of course you can turn around the code. So you don't loop over the days in the interval, but over the holidays. Then you just calculate the number of week-days in the interval(should be simple math) and subtract the number of holidays that fall on a week-day.

If it's really necessary you can pre-calculate the workdays since some fixed date, and then subtract the lookup result from the beginning of the period from the lookup result from the end of the period.

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
0

I would investigate an algorithm like the following. (Sorry no code provided.)

  1. Count the number of full 7-day weeks between your start and end date. You should be able to do this with .NET DateTime and TimeSpan objects.
  2. For each full 7-day week, add 5 days to your result.
  3. Figure out the partial weeks including your start and end dates.
  4. Loop through your holidays and reduce your result by 1 for each holiday between your start and end date. Looping is better here because there are likely far fewer holidays to loop over than days between your start and end date.

Have fun!

EDIT: For source code, check out this answer: Calculate the number of business days between two dates?

Community
  • 1
  • 1
John Bledsoe
  • 17,142
  • 5
  • 42
  • 59
  • thanks John, as you see, my question has code that is no worse than that provided in the answer that you are referring to. :) – akonsu Dec 06 '10 at 19:51
  • @akonsu: No, this answer is not equivalent (either in concept or in practical performance) to your code. Your code iterates every day in between the two dates. As the date range gets wider, your code will grow linearly with that size, whereas this approach is basically constant. – Adam Robinson Dec 06 '10 at 20:05
  • i was talking about the answer in the similar question: http://stackoverflow.com/questions/1617049/calculate-the-number-of-business-days-between-two-dates – akonsu Dec 06 '10 at 20:07
  • @akonsu: He was likely referring to the second answer, not the first: http://stackoverflow.com/questions/1617049/calculate-the-number-of-business-days-between-two-dates/1619375#1619375 – Adam Robinson Dec 06 '10 at 20:13
  • Right all, I updated my answer to point to the proper post in the thread. – John Bledsoe Dec 06 '10 at 21:04
0

if you want faster code, don't loop over each day in the range:

remove from your list of holidays all holidays that fall on sunday or saturday, then use the timespan methods to give you the number of days between the two dates. With a little math (think about integer division by 7) you can get the number of mon-thursday days in that range, subtract the number of holidays that don't fall on the weekend from that number and you are done.

Assaf
  • 1,336
  • 1
  • 10
  • 17
  • so it looks you are saying that i need to only loop over the list of holidays (to find which ones are inside my date range) and get rid of my loop over the time interval. – akonsu Dec 06 '10 at 19:48
  • @akonsu - thats right; the list of holidays is (presumably) a much smaller list to loop over. The rest is just a few math operators. – Assaf Dec 07 '10 at 18:12
0

Just roll with it as is. This is not going to waste much time since the bounds are small. When you have some working code, move on. No need to mercilessly optimise code for no reason.

jambox
  • 584
  • 4
  • 15
0

just because I started this as a fun puzzle, here's the code:

    [Test]
    public void TestDateTime() {
        var start = DateTime.Now.Date;
        var end = DateTime.Now.Date.AddDays(35);
        var workdays = (end - start).Days - ((end - start).Days/7)*2 
            - (((end - start).Days%7==0)?0:(((int)start.DayOfWeek==0)?1:Math.Max(Math.Min((int)start.DayOfWeek + (end - start).Days%7 - 6, 2), 0)));
        new []{DateTime.Now.AddDays(19), DateTime.Now.AddDays(20)}.ToList().ForEach(
            x => { if (x.DayOfWeek != DayOfWeek.Saturday && x.DayOfWeek != DayOfWeek.Sunday) workdays--; });
        Console.Out.WriteLine("workdays = {0}", workdays);
    }

Christmas day and Boxing day are included as holidays.

Adam Dymitruk
  • 124,556
  • 26
  • 146
  • 141
  • this is cool, thanks. this code (as well as the code that i found in the related question) does not work for partial days though. – akonsu Dec 07 '10 at 02:33
  • can you explain what you want for partial days? It should be easy to adapt what I have here. – Adam Dymitruk Dec 08 '10 at 22:01