C#: Adding working days from a cetain date
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);
CSharpNoob
Updated on October 06, 2022Comments
-
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 over 13 yearsJon, 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 over 13 yearsI 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 over 13 yearsI 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 over 13 yearsbut the datetime parameter(start) can also be sunday and saturrday .. :(.. if saturday and sunday, it must return wednesday of the next week,
-
CSharpNoob over 13 yearsIs it more efficient than mr. Jon Skeet's suggestion?
-
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 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 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 over 13 yearsAs CSharpNoob pointed out, this code doesn't quite work correctly, but I really like the approach of using an extension method for this.
-
CSharpNoob over 13 yearswhats the difference between MyAddWorkingDays and AddWorkingDays in your code?
-
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 over 13 yearstried it but it only adds 1 working day if the specified day is Mondays-Fri, and it adds 2 if sat/sun.. =)
-
CSharpNoob over 13 yearsThanks Jon. I'll convert it to extension method .. =)
-
garik over 13 years@CSharpNoob the first code has typo "currentDate" (I have already fixed it). Try the second one! :)
-
CSharpNoob over 13 yearsThanks 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 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 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 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 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 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 over 13 yearsyes, its wrong, I didn`t test well before post. sorry guys, my fault.
-
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 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 over 13 years@Jon Skeet oh, thanks! yes of course, you are right. it was expected result. your one is faster, sorry.
-
Lazarus over 13 years@Jon-Skeet: I claim the n th amendment "Lack of Coffee" and retreat to the corner :)
-
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 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 over 8 yearsI get it. Especially considering public holidays is really getting complex in calculating version. Thanks for the insight!
-
tocqueville almost 8 yearsIsn't it a bit too complicated? I think it can be done in a much simpler way