C#: Adding working days from a cetain date

12,223

Solution 1

If you don't need to consider holidays, I would suggest you do something like this:

public static DateTime AddWorkingDays(DateTime specificDate,
                                      int workingDaysToAdd)
{
    int completeWeeks = workingDaysToAdd / 5;
    DateTime date = specificDate.AddDays(completeWeeks * 7);
    workingDaysToAdd = workingDaysToAdd % 5;
    for (int i = 0; i < workingDaysToAdd; i++)
    {
        date = date.AddDays(1);
        while (!IsWeekDay(date))
        {
            date = date.AddDays(1);
        }
    }
    return date;
}

private static bool IsWeekDay(DateTime date)
{
    DayOfWeek day = date.DayOfWeek;
    return day != DayOfWeek.Saturday && day != DayOfWeek.Sunday;
}

It's inefficient, but easy to understand. For an efficient version, you'd work out the number of complete weeks to add as before, but then have a mapping from any "current day of week" and "working days left to add" to "number of actual days to add". Then you could just work out the total number of days to add, and do it in one call.

EDIT: In terms of the level of inefficiency... it's really not very bad. It'll only perform manual "is this a weekend" checks for up to 4 days, which isn't too bad. In particular, despite igor's (current at the time of posting) claims, it's rather faster than his approach, flawed benchmarks notwithstanding ;)

Note that it may not handle negative inputs yet - I haven't checked.

One of the reasons behind the approach I'm using is that it doesn't rely on either me or the code reader knowing what the values in the DayOfWeek enum are. I don't care whether it's 0-6, 1-7, Monday-Sunday, Saturday-Friday... or even if there are completely bizarre values. I only compare for equality, which makes the code more "obviously correct".

Solution 2

A cool way (i think) is put that in a extension method, like:

public static class DateTimeExtensions
{
    public static DateTime AddWorkingDays(this DateTime self, int days)
    {
        self = self.AddDays(days);
        while (self.DayOfWeek == DayOfWeek.Saturday || self.DayOfWeek == DayOfWeek.Sunday)
        {
            self = self.AddDays(1);
        }

        return self;
    }
}

so your final code will look like:

specificDate.AddWorkingDays(3);
Share:
12,223
CSharpNoob
Author by

CSharpNoob

Updated on October 06, 2022

Comments

  • CSharpNoob
    CSharpNoob over 1 year

    I have trouble doing this. I'm creating a method that add working days on a specific date. for example, I want to add 3 working days to sept 15, 2010 (Wednesday), the method would return sept 20 (Monday next week). it disregards saturday and sunday because its non-working day..

    Something like this in C#:

    DateTime AddWorkingDays(DateTime specificDate, int workingDaysToAdd)
    {
       return specificDate + (workingDaysToAdd - (all saturdays and sundays))
    }
    

    I don't consider special holidays on the computations, i just literally want to add days except saturday and sundays.. Thanks in advance! =)

  • Lazarus
    Lazarus over 13 years
    Jon, would it be more efficient to use workingDaysToAdd = workingDaysToAdd % 5; date = date.AddDays(workingDaysToAdd); if (date.DayOfWeek == DayOfWeek.Saturday) date.AddDays(2); if (date.DayOfWeek == DayOfWeek.Sunday) date.AddDays(1); rather than the loop construct? Just curious.
  • CSharpNoob
    CSharpNoob over 13 years
    I tested it and it works to what I exactly need. Did you do it on the fly? in less than 5 minutes since I posted it.. man, that's quite awesome.. but I would welcome more efficient answers.. thanks btw..
  • Anthony Pegram
    Anthony Pegram over 13 years
    I wouldn't say it's inefficient, but all things are relative. The answer accounts for weeks up front, so the most it will ever have to loop is 6 times (max 4 for workingDays % 5, max 2 for weekend days). Not a tremendous hit.
  • CSharpNoob
    CSharpNoob over 13 years
    but the datetime parameter(start) can also be sunday and saturrday .. :(.. if saturday and sunday, it must return wednesday of the next week,
  • CSharpNoob
    CSharpNoob over 13 years
    Is it more efficient than mr. Jon Skeet's suggestion?
  • Anthony Pegram
    Anthony Pegram over 13 years
    @CSharpNoob, no. This is essentially the same logic without handling entire weeks up front. So instead of a loop that could be from 1 to 4 (plus up to two weekend days), you have a loop that will be from 1 to n (plus all weekend days)
  • Jon Skeet
    Jon Skeet over 13 years
    @Lazarus: That code won't work if you try to add 3 days to Friday - it'll give Monday instead of Wednesday.
  • Jon Skeet
    Jon Skeet over 13 years
    @CSharpNoob: Yes. Glad it works. I can't say I tested or even compiled it... :( @Anthony: Sure, it's likely to be fast enough in most cases... but there's room to improve if it ever becomes a bottleneck.
  • Patrick
    Patrick over 13 years
    As CSharpNoob pointed out, this code doesn't quite work correctly, but I really like the approach of using an extension method for this.
  • CSharpNoob
    CSharpNoob over 13 years
    whats the difference between MyAddWorkingDays and AddWorkingDays in your code?
  • garik
    garik over 13 years
    @CSharpNoob MyAddWorkingDays is main, AddWorkingDays is mr. Jon Skeet's one. I have not tested other ones. Try them all if you need
  • CSharpNoob
    CSharpNoob over 13 years
    tried it but it only adds 1 working day if the specified day is Mondays-Fri, and it adds 2 if sat/sun.. =)
  • CSharpNoob
    CSharpNoob over 13 years
    Thanks Jon. I'll convert it to extension method .. =)
  • garik
    garik over 13 years
    @CSharpNoob the first code has typo "currentDate" (I have already fixed it). Try the second one! :)
  • CSharpNoob
    CSharpNoob over 13 years
    Thanks Igor, I have tested it again and it now works perfectly, it's shorter than Jon's and performance wise as you say, its better.. and I'll convert it to extension method.. thanks alot man..
  • Lazarus
    Lazarus over 13 years
    @Jon-Skeet: Cracked it in the end if ((int)specificDate.DayOfWeek + workingdaysToAdd > 5) specificDate = specificDate.AddDays(2); replaces the loop.
  • Jon Skeet
    Jon Skeet over 13 years
    @Lazarus: Yup, something like that will work... but I'd find it harder to read. It's not as obviously right... in particular, you need to know the enum values. I don't know off the top of my head what the values for Saturday and Sunday are... which is why I've written the code to not care.
  • Lazarus
    Lazarus over 13 years
    @Jon-Skeet: You've shattered my illusions... something you don't know off the top of your head! I will give you that this is framework and not C# so perhaps your status remains intact, albeit a little wobbly :) Sunday = 0, Saturday = 6, so anything bigger than 5 (Friday) means we hit a weekend and need to insert 2 days.
  • Jon Skeet
    Jon Skeet over 13 years
    @Lazarus: There's a bigger point here though... do you want to rely on everyone who reads the code to know the enum values in order to understand what it's doing? That's what I'm trying to get away from.
  • Jon Skeet
    Jon Skeet over 13 years
    @igor: Your benchmark is horribly flawed. Try swapping round which result you print first... the results switch too. You're including JIT compilation, including JIT compilation of Console.WriteLine. Try running both versions 10000 times in a loop, even for just 300 days, and you'll find my version is much faster than yours... and so it should be, given that you're testing whether every single day is on the weekend, rather than just a few of them.
  • Leo
    Leo over 13 years
    yes, its wrong, I didn`t test well before post. sorry guys, my fault.
  • Lazarus
    Lazarus over 13 years
    @Jon-Skeet: I see what you are saying & I do agree that you'd have to resort to documentation to be certain of what I was doing where you could sit down with pen and paper to walk through your loop to understand it although on paper for Friday + 3 Working days I get Tuesday as the result. @i==0 (date = Friday, date.AddDays(1) = Saturday, !IsWeekday, date.AddDays(1) = Sunday), @i==1 (date = Sunday, date.AddDays(1) = Monday, IsWeekday), @i==2 (date = Monday, date.AddDays(1) = Tuesday), return date = Tuesday What am I missing?
  • Jon Skeet
    Jon Skeet over 13 years
    @Lazarus: You're missing the fact that it's a while loop, not just a single test. Basically I add a day, and then skip over the weekend completely.
  • garik
    garik over 13 years
    @Jon Skeet oh, thanks! yes of course, you are right. it was expected result. your one is faster, sorry.
  • Lazarus
    Lazarus over 13 years
    @Jon-Skeet: I claim the n th amendment "Lack of Coffee" and retreat to the corner :)
  • Anmol Gupta
    Anmol Gupta over 8 years
    @JonSkeet Here is an tested algorithm to do it without looping over: stackoverflow.com/a/33943576/4389984 (second solution) Apart from having knowledge of day of the week mapping (1-7 ) and readability, do you see any flaw in the logic? Thanks!
  • Jon Skeet
    Jon Skeet over 8 years
    @Anmol: The lack of readability makes it much harder to search for flaws, to be honest. That's why I like the looping option - it's dead easy to understand, and unless you know you are adding a lot of work days (not the case in this question) I'd favour readability over efficiency. Additionally, it's really easy to go from a naive implementation which ignores public holidays to a naive implementation which includes public holidays, given a list of public holidays - it's much harder to do the same with a calculating version.
  • Anmol Gupta
    Anmol Gupta over 8 years
    I get it. Especially considering public holidays is really getting complex in calculating version. Thanks for the insight!
  • tocqueville
    tocqueville almost 8 years
    Isn't it a bit too complicated? I think it can be done in a much simpler way