Compute the DateTime of an upcoming weekday - c#

How can I get the date of next Tuesday?
In PHP, it's as simple as strtotime('next tuesday');.
How can I achieve something similar in .NET

As I've mentioned in the comments, there are various things you could mean by "next Tuesday", but this code gives you "the next Tuesday to occur, or today if it's already Tuesday":
DateTime today = DateTime.Today;
// The (... + 7) % 7 ensures we end up with a value in the range [0, 6]
int daysUntilTuesday = ((int) DayOfWeek.Tuesday - (int) today.DayOfWeek + 7) % 7;
DateTime nextTuesday = today.AddDays(daysUntilTuesday);
If you want to give "a week's time" if it's already Tuesday, you can use:
// This finds the next Monday (or today if it's Monday) and then adds a day... so the
// result is in the range [1-7]
int daysUntilTuesday = (((int) DayOfWeek.Monday - (int) today.DayOfWeek + 7) % 7) + 1;
... or you could use the original formula, but from tomorrow:
DateTime tomorrow = DateTime.Today.AddDays(1);
// The (... + 7) % 7 ensures we end up with a value in the range [0, 6]
int daysUntilTuesday = ((int) DayOfWeek.Tuesday - (int) tomorrow.DayOfWeek + 7) % 7;
DateTime nextTuesday = tomorrow.AddDays(daysUntilTuesday);
EDIT: Just to make this nice and versatile:
public static DateTime GetNextWeekday(DateTime start, DayOfWeek day)
{
// The (... + 7) % 7 ensures we end up with a value in the range [0, 6]
int daysToAdd = ((int) day - (int) start.DayOfWeek + 7) % 7;
return start.AddDays(daysToAdd);
}
So to get the value for "today or in the next 6 days":
DateTime nextTuesday = GetNextWeekday(DateTime.Today, DayOfWeek.Tuesday);
To get the value for "the next Tuesday excluding today":
DateTime nextTuesday = GetNextWeekday(DateTime.Today.AddDays(1), DayOfWeek.Tuesday);

This should do the trick:
static DateTime GetNextWeekday(DayOfWeek day)
{
DateTime result = DateTime.Now.AddDays(1);
while( result.DayOfWeek != day )
result = result.AddDays(1);
return result;
}

There are less verbose and more clever/elegant solutions to this problem, but the following C# function works really well for a number of situations.
/// <summary>
/// Find the closest weekday to the given date
/// </summary>
/// <param name="includeStartDate">if the supplied date is on the specified day of the week, return that date or continue to the next date</param>
/// <param name="searchForward">search forward or backward from the supplied date. if a null parameter is given, the closest weekday (ie in either direction) is returned</param>
public static DateTime ClosestWeekDay(this DateTime date, DayOfWeek weekday, bool includeStartDate = true, bool? searchForward=true)
{
if (!searchForward.HasValue && !includeStartDate)
{
throw new ArgumentException("if searching in both directions, start date must be a valid result");
}
var day = date.DayOfWeek;
int add = ((int)weekday - (int)day);
if (searchForward.HasValue)
{
if (add < 0 && searchForward.Value)
{
add += 7;
}
else if (add > 0 && !searchForward.Value)
{
add -= 7;
}
else if (add == 0 && !includeStartDate)
{
add = searchForward.Value ? 7 : -7;
}
}
else if (add < -3)
{
add += 7;
}
else if (add > 3)
{
add -= 7;
}
return date.AddDays(add);
}

#Jon Skeet good answer.
For previous Day:
private DateTime GetPrevWeekday(DateTime start, DayOfWeek day) {
// The (... - 7) % 7 ensures we end up with a value in the range [0, 6]
int daysToRemove = ((int) day - (int) start.DayOfWeek - 7) % 7;
return start.AddDays(daysToRemove);
}
Thanks!!

DateTime nextTuesday = DateTime.Today.AddDays(((int)DateTime.Today.DayOfWeek - (int)DayOfWeek.Tuesday) + 7);

Very simple sample to include or exclude current date, you specify the date and the day the week you are interested in.
public static class DateTimeExtensions
{
/// <summary>
/// Gets the next date.
/// </summary>
/// <param name="date">The date to inspected.</param>
/// <param name="dayOfWeek">The day of week you want to get.</param>
/// <param name="exclDate">if set to <c>true</c> the current date will be excluded and include next occurrence.</param>
/// <returns></returns>
public static DateTime GetNextDate(this DateTime date, DayOfWeek dayOfWeek, bool exclDate = true)
{
//note: first we need to check if the date wants to move back by date - Today, + diff might move it forward or backwards to Today
//eg: date - Today = 0 - 1 = -1, so have to move it forward
var diff = dayOfWeek - date.DayOfWeek;
var ddiff = date.Date.Subtract(DateTime.Today).Days + diff;
//note: ddiff < 0 : date calculates to past, so move forward, even if the date is really old, it will just move 7 days from date passed in
//note: ddiff >= (exclDate ? 6 : 7) && diff < 0 : date is into the future, so calculated future weekday, based on date
if (ddiff < 0 || ddiff >= (exclDate ? 6 : 7) && diff < 0)
diff += 7;
//note: now we can get safe values between 0 - 6, especially if past dates is being used
diff = diff % 7;
//note: if diff is 0 and we are excluding the date passed, we will add 7 days, eg: 1 week
diff += diff == 0 & exclDate ? 7 : 0;
return date.AddDays(diff);
}
}
some test cases
[TestMethod]
public void TestNextDate()
{
var date = new DateTime(2013, 7, 15);
var start = date;
//testing same month - forwardOnly
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Tuesday)); //16
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Wednesday)); //17
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Thursday)); //18
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Friday)); //19
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Saturday)); //20
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Sunday)); //21
Assert.AreEqual(start.AddDays(1), date.GetNextDate(DayOfWeek.Monday)); //22
//testing same month - include date
Assert.AreEqual(start = date, date.GetNextDate(DayOfWeek.Monday, false)); //15
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Tuesday, false)); //16
Assert.AreEqual(start.AddDays(1), date.GetNextDate(DayOfWeek.Wednesday, false)); //17
//testing month change - forwardOnly
date = new DateTime(2013, 7, 29);
start = date;
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Tuesday)); //30
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Wednesday)); //31
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Thursday)); //2013/09/01-month increased
Assert.AreEqual(start.AddDays(1), date.GetNextDate(DayOfWeek.Friday)); //02
//testing year change
date = new DateTime(2013, 12, 30);
start = date;
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Tuesday)); //31
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Wednesday)); //2014/01/01 - year increased
Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Thursday)); //02
}

DateTime nexttuesday=DateTime.Today.AddDays(1);
while(nexttuesday.DayOfWeek!=DayOfWeek.Tuesday)
nexttuesday = nexttuesday.AddDays(1);

Now in oneliner flavor - in case you need to pass it as parameter into some mechanism.
DateTime.Now.AddDays(((int)yourDate.DayOfWeek - (int)DateTime.Now.DayOfWeek + 7) % 7).Day
In this specific case:
DateTime.Now.AddDays(((int)DayOfWeek.Tuesday - (int)DateTime.Now.DayOfWeek + 7) % 7).Day

It could be an extension also, it all depends
public static class DateTimeExtensions
{
public static IEnumerable<DateTime> Next(this DateTime date, DayOfWeek day)
{
// This loop feels expensive and useless, but the point is IEnumerable
while(true)
{
if (date.DayOfWeek == day)
{
yield return date;
}
date = date.AddDays(1);
}
}
}
Usage
var today = DateTime.Today;
foreach(var monday in today.Next(DayOfWeek.Monday))
{
Console.WriteLine(monday);
Console.ReadKey();
}

I want to get next day from this time include today
also I want it to be at 12:00 AM
public static DateTime GetNextWeekday(DateTime start, DayOfWeek day , bool includetoday = false) {
if (includetoday && start.DayOfWeek == day) return start.Date;
int daysToAdd = ((int)day - (int)start.DayOfWeek + 7) % 7;
return start.Date.AddDays(daysToAdd);
}

Objective C Version:
+(NSInteger) daysUntilNextWeekday: (NSDate*)startDate withTargetWeekday: (NSInteger) targetWeekday
{
NSInteger startWeekday = [[NSCalendar currentCalendar] component:NSCalendarUnitWeekday fromDate:startDate];
return (targetWeekday - startWeekday + 7) % 7;
}

Related

How to find the 3rd Friday in a month with C#?

Given a date (of type DateTime), how do I find the 3rd Friday in the month of that date?
I'm going to repeat my answer from here with one little addition.
The language-agnostic version:
To get the first particular day of the month, start with the first day of the month: yyyy-mm-01. Use whatever function is available to give a number corresponding to the day of the week; in C# this would be DateTime.DayOfWeek. Subtract that number from the day you are looking for; for example, if the first day of the month is Wednesday (3) and you're looking for Friday (5), subtract 3 from 5, leaving 2. If the answer is negative, add 7. Finally add that to the first of the month; for my example, the first Friday would be the 3rd.
To get the last Friday of the month, find the first Friday of the next month and subtract 7 days.
To get the 3rd Friday of the month, add 14 days to the first Friday.
I haven't tested this, but since the third Friday can't possibly occur before the 15th of the month, create a new DateTime, then just increment until you get to a Friday.
DateTime thirdFriday= new DateTime(yourDate.Year, yourDate.Month, 15);
while (thirdFriday.DayOfWeek != DayOfWeek.Friday)
{
thirdFriday = thirdFriday.AddDays(1);
}
I followed User:Mark Ransom's algorithm and wrote a generalized day finder. For example to get the 3rd friday of december 2013,
int thirdFriday = DayFinder.FindDay(2013, 12, DayOfWeek.Friday, 3);
And here is the function definition. It doesn't have any iterative loops, so its efficient.
public class DayFinder
{
//For example to find the day for 2nd Friday, February, 2016
//=>call FindDay(2016, 2, DayOfWeek.Friday, 2)
public static int FindDay(int year, int month, DayOfWeek Day, int occurance)
{
if (occurance <= 0 || occurance > 5)
throw new Exception("Occurance is invalid");
DateTime firstDayOfMonth = new DateTime(year, month, 1);
//Substract first day of the month with the required day of the week
var daysneeded = (int)Day - (int)firstDayOfMonth.DayOfWeek;
//if it is less than zero we need to get the next week day (add 7 days)
if (daysneeded < 0) daysneeded = daysneeded + 7;
//DayOfWeek is zero index based; multiply by the Occurance to get the day
var resultedDay = (daysneeded + 1) + (7 * (occurance - 1));
if (resultedDay > (firstDayOfMonth.AddMonths(1) - firstDayOfMonth).Days)
throw new Exception(String.Format("No {0} occurance(s) of {1} in the required month", occurance, Day.ToString()));
return resultedDay;
}
}
Probably best to abstract this to a method to do any date/day combination:
(Extension Method)
public static bool TryGetDayOfMonth(this DateTime instance,
DayOfWeek dayOfWeek,
int occurance,
out DateTime dateOfMonth)
{
if (instance == null)
{
throw new ArgumentNullException("instance");
}
if (occurance <= 0 || occurance > 5)
{
throw new ArgumentOutOfRangeException("occurance", "Occurance must be greater than zero and less than 6.");
}
bool result;
dateOfMonth = new DateTime();
// Change to first day of the month
DateTime dayOfMonth = instance.AddDays(1 - instance.Day);
// Find first dayOfWeek of this month;
if (dayOfMonth.DayOfWeek > dayOfWeek)
{
dayOfMonth = dayOfMonth.AddDays(7 - (int)dayOfMonth.DayOfWeek + (int)dayOfWeek);
}
else
{
dayOfMonth = dayOfMonth.AddDays((int)dayOfWeek - (int)dayOfMonth.DayOfWeek);
}
// add 7 days per occurance
dayOfMonth = dayOfMonth.AddDays(7 * (occurance - 1));
// make sure this occurance is within the original month
result = dayOfMonth.Month == instance.Month;
if (result)
{
dateOfMonth = dayOfMonth;
}
return result;
}
Results:
DateTime myDate = new DateTime(2013, 1, 1)
DateTime dateOfMonth;
myDate.TryGetDayOfMonth(DayOfWeek.Sunday, 1, out dateOfMonth)
// returns: true; dateOfMonth = Sunday, 1/6/2013
myDate.TryGetDayOfMonth(DayOfWeek.Sunday, 4, out dateOfMonth)
// returns: true; dateOfMonth = Sunday, 1/27/2013
myDate.TryGetDayOfMonth(DayOfWeek.Sunday, 5, out dateOfMonth)
// returns: false;
myDate.TryGetDayOfMonth(DayOfWeek.Wednesday, 1, out dateOfMonth)
// returns: true; dateOfMonth = Wednesday, 1/2/2013
myDate.TryGetDayOfMonth(DayOfWeek.Wednesday, 4, out dateOfMonth)
// returns: true; dateOfMonth = Wednesday, 1/23/2013
myDate.TryGetDayOfMonth(DayOfWeek.Wednesday, 5, out dateOfMonth)
// returns: true; dateOfMonth = Wednesday, 1/30/2013
// etc
Old post, but I found remarkably few decent answers online for this surely quite common problem! Mark Ransom's answer should be the last word on this algorithm-wise, but here is a C# helper class (in this case I think clearer than extensions) for anyone who wants a quick answer to the common problems of "first day of week in month", "xth day of week in month" and "last day of week in month".
I modified it to return DateTime.MinValue if the Xth day of the week falls outside the provided month rather than wrapping to the next month, because that to me seems more useful.
I've thrown in a LINQPad-runnable example program too.
void Main()
{
DayOfWeek dow = DayOfWeek.Friday;
int y = 2014;
int m = 2;
String.Format("First {0}: {1}", new object[] { dow, DateHelper.FirstDayOfWeekInMonth(y, m, dow) }).Dump();
"".Dump();
String.Format("Last {0}: {1}", new object[] { dow, DateHelper.LastDayOfWeekInMonth(y, m, dow) }).Dump();
"".Dump();
for(int i = 1; i <= 6; i++)
String.Format("{0} #{1}: {2}", new object[] { dow, i, DateHelper.XthDayOfWeekInMonth(y, m, dow, i) }).Dump();
}
public class DateHelper
{
public static DateTime FirstDayOfWeekInMonth(int year, int month, DayOfWeek day)
{
DateTime res = new DateTime(year, month, 1);
int offset = -(res.DayOfWeek - day);
if (offset < 0)
offset += 7;
res = res.AddDays(offset);
return res;
}
public static DateTime LastDayOfWeekInMonth(int year, int month, DayOfWeek day)
{
DateTime dt = new DateTime(year, month, 1).AddMonths(1);
DateTime res = FirstDayOfWeekInMonth(dt.Year, dt.Month, day);
res = res.AddDays(-7);
return res;
}
public static DateTime XthDayOfWeekInMonth(int year, int month, DayOfWeek day, int x)
{
DateTime res = DateTime.MinValue;
if (x > 0)
{
res = FirstDayOfWeekInMonth(year, month, day);
if (x > 1)
res = res.AddDays((x - 1) * 7);
res = res.Year == year && res.Month == month ? res : DateTime.MinValue;
}
return res;
}
}
Prints:
First Friday: 07/02/2014 00:00:00
Last Friday: 28/02/2014 00:00:00
Friday #1: 07/02/2014 00:00:00
Friday #2: 14/02/2014 00:00:00
Friday #3: 21/02/2014 00:00:00
Friday #4: 28/02/2014 00:00:00
Friday #5: 01/01/0001 00:00:00
Friday #6: 01/01/0001 00:00:00
Slightly more optimized version:
DateTime Now = DateTime.Now;
DateTime TempDate = new DateTime(Now.Year, Now.Month, 1);
// find first friday
while (TempDate.DayOfWeek != DayOfWeek.Friday)
TempDate = TempDate.AddDays(1);
// add two weeks
TempDate = TempDate.AddDays(14);
This is a version that uses LINQ and functional programming style.
It works like this.
First, take all of the days of the month. Then select only the ones of the right day (Friday). Finally take the nth (3rd) entry and return.
// dt: The date to start from (usually DateTime.Now)
// n: The nth occurance (3rd)
// weekday: the day of the week to look for
public DateTime GetNthWeekdayOfMonth(DateTime dt, int n, DayOfWeek weekday)
{
var days = Enumerable.Range(1, DateTime.DaysInMonth(dt.Year, dt.Month)).Select(day => new DateTime(dt.Year, dt.Month, day));
var weekdays = from day in days
where day.DayOfWeek == weekday
orderby day.Day ascending
select day;
int index = n - 1;
if (index >= 0 && index < weekdays.Count())
return weekdays.ElementAt(index);
else
throw new InvalidOperationException("The specified day does not exist in this month!");
}
My reasoning goes like this
the 15th is the first possible "third Friday" (1,8,15)
therefore we're looking for the first Friday on or after the 15th
DayOfWeek is an enumeration starting with 0 for Sunday
Therefore you have to add an offet of 5-(int)baseDay.DayOfWeek to the 15th
Except that the above offset can be negative, which we fix by adding 7, then doing modulo 7.
In code:
public static DateTime GetThirdFriday(int year, int month)
{
DateTime baseDay = new DateTime(year, month, 15);
int thirdfriday = 15 + ((12 - (int)baseDay.DayOfWeek) % 7);
return new DateTime(year, month, thirdfriday);
}
Since there are only 7 possible results, you could also do this:
private readonly static int[] thirdfridays =
new int[] { 20, 19, 18, 17, 16, 15, 21 };
public static int GetThirdFriday(int year, int month)
{
DateTime baseDay = new DateTime(year, month, 15);
return thirdfridays[(int)baseDay.DayOfWeek];
}
I pass this the DateTime for the start of the month I am looking at.
private DateTime thirdSunday(DateTime timeFrom)
{
List<DateTime> days = new List<DateTime>();
DateTime testDate = timeFrom;
while (testDate < timeFrom.AddMonths(1))
{
if (testDate.DayOfWeek == DayOfWeek.Friday)
{
days.Add(testDate);
}
testDate = testDate.AddDays(1);
}
return days[2];
}
I know of no clean/built in way of doing this. But it's not too hard to code up:
DateTime now = DateTime.Now;
for (int i = 0; i < 7; ++i)
{
DateTime d = new DateTime(now.Year, now.Month, i+1);
if (d.DayOfWeek == DayOfWeek.Friday)
{
return d.AddDays(14);
}
}
public DateTime GetThirdThursday(DateTime now)
{
DateTime ThirdThursday;
now = DateTime.Now;
string wkday;
DateTime firstday = new DateTime(now.Year, now.Month, 1);
ThirdThursday = firstday.AddDays(15);
// ThirdThursday = now.AddDays((now.Day - 1) * -1).AddDays(14);
wkday = ThirdThursday.DayOfWeek.ToString();
while (wkday.CompareTo("Thursday") < 0)
{
ThirdThursday.AddDays(1);
}
return ThirdThursday;
}
Late to the game but here's my solution to add DateTime extension functionality which accounts for the January to December issue when subtracting occurrence values and accounts for if the occurrence is 5 - which in my case should return the last occurrence which would either be number 4 or 5 depending on where the day lands in the month:
public static DateTime NthOf(this DateTime CurDate, int Occurrence, DayOfWeek Day)
{
//Last day of month if 5 - return last day.
if (Occurrence == 5)
{
return LastDayOfMonth(CurDate, Day);
}
var fday = new DateTime(CurDate.Year, CurDate.Month, 1, CurDate.Hour, CurDate.Minute, CurDate.Second);
var firstoccurrence = fday.DayOfWeek == Day ? fday : fday.AddDays(Day - fday.DayOfWeek);
// CurDate = 2011.10.1 Occurance = 1, Day = Friday >> 2011.09.30 FIX.
if (firstoccurrence.Month < CurDate.Month)
{
Occurrence = Occurrence + 1;
} else if (firstoccurrence.Month == 12 && CurDate.Month == 1)
{
Occurrence = Occurrence + 1;
}
return firstoccurrence.AddDays(7 * (Occurrence - 1));
}
public static DateTime LastDayOfMonth(this DateTime CurDate, DayOfWeek Day)
{
DateTime EndOfMonth = new DateTime(CurDate.Year, CurDate.Month, 1).AddMonths(1).AddDays(-1);
while (EndOfMonth.DayOfWeek != Day)
{
EndOfMonth = EndOfMonth.AddDays(-1);
}
return EndOfMonth;
}
The you can call your method with something like this:
Console.WriteLine(DateTime.Now.NthOf(3, DayOfWeek.Friday).ToString());
This would return the third Friday of the current month and log it to the console as a string value. It extends from DateTime nicely and does not require any usage of a static helper class or any additional moving parts.
int numday = 0;
int dayofweek = 5; //friday
DateTime thirdfriday;
for (int i = 0; i < (date.AddMonths(1) - date).Days && numday <3; i++)
{
if ((int)date.AddDays(i).DayOfWeek == dayofweek)
{
numday++;
}
if (numday == 3)
{
thirdfriday = date.AddDays(i);
}
}
Sorry to jump in late on this... Might help someone else tho.
Begin rant: Loops, yuck. Too much code, yuck. Not Generic Enough, yuck.
Here's a simple function with a free overload.
public DateTime DateOfWeekOfMonth(int year, int month, DayOfWeek dayOfWeek, byte weekNumber)
{
DateTime tempDate = new DateTime(year, month, 1);
tempDate = tempDate.AddDays(-(tempDate.DayOfWeek - dayOfWeek));
return
tempDate.Day > (byte)DayOfWeek.Saturday
? tempDate.AddDays(7 * weekNumber)
: tempDate.AddDays(7 * (weekNumber - 1));
}
public DateTime DateOfWeekOfMonth(DateTime sender, DayOfWeek dayOfWeek, byte weekNumber)
{
return DateOfWeekOfMonth(sender.Year, sender.Month, dayOfWeek, weekNumber);
}
Your usage:
DateTime thirdFridayOfMonth = DateOfWeekOfMonth(DateTime.Now, DayOfWeek.Friday, 3);
Here's my algorithm:
Find the number of days until the upcoming Friday.
Initialize a counter and set it to 1. Subtract seven days from the date returned from [1], then compare the month from the date returned against the date returned from (1).
If the months are not equal, return the counter from [2].
If the months are equal, recurse into [2] and add 1 to the counter created in [2].
The counter will give you the nth Friday of the month for that date (or its upcoming Friday).
Following works great, no validation for occurrence is provided. You can find any nth day for the given date month either from start or last. Provide minus occurrence value if you are looking for from the last.
public static DateTime GetDayOfMonth(DateTime dateValue, DayOfWeek dayOfWeek, int occurance)
{
List<DateTime> dayOfWeekRanges = new List<DateTime>();
//move to the first of th month
DateTime startOfMonth = new DateTime(dateValue.Year, dateValue.Month, 1);
//move startOfMonth to the dayOfWeek requested
while (startOfMonth.DayOfWeek != dayOfWeek)
startOfMonth = startOfMonth.AddDays(1);
do
{
dayOfWeekRanges.Add(startOfMonth);
startOfMonth = startOfMonth.AddDays(7);
} while (startOfMonth.Month == dateValue.Month);
bool fromLast = occurance < 0;
if (fromLast)
occurance = occurance * -1;
if (fromLast)
return dayOfWeekRanges[dayOfWeekRanges.Count - occurance];
else
return dayOfWeekRanges[occurance - 1];
}
Here is my two cents...
An optimized solution without unnecessary loops or tests :
public static DateTime ThirdFridayOfMonth(DateTime dateTime)
{
int day = dateTime.Day;
return dateTime.AddDays(21 - day - ((int)dateTime.DayOfWeek + 37 - day) % 7);
}
I wrote extended version of #justcoding121's code that can get from the last day of the month. I don't know this algorithm is right, but it works so far.
public static int? GetNthDayOfWeekInMonth(int year, int month, DayOfWeek dow, int weekNumOfMonth)
{
if (weekNumOfMonth < -5 || weekNumOfMonth == 0 || weekNumOfMonth > 5)
throw new ArgumentOutOfRangeException("weekNumOfMonth", $"must be between 1~5 or -1~-5. ({weekNumOfMonth})");
int daysOfMonth = DateTime.DaysInMonth(year, month);
if (weekNumOfMonth > 0)
{
var firstDay = new DateTime(year, month, 1);
var firstDayOfTargetDOW = (int)dow - (int)firstDay.DayOfWeek;
if (firstDayOfTargetDOW < 0)
firstDayOfTargetDOW += 7;
var resultedDay = (firstDayOfTargetDOW + 1) + (7 * (weekNumOfMonth - 1));
if (resultedDay > daysOfMonth)
return null;
return resultedDay;
}
else
{
var lastDay = new DateTime(year, month, daysOfMonth);
var firstDayOfTargetDOW = (int)lastDay.DayOfWeek - (int)dow;
if (firstDayOfTargetDOW < 0)
firstDayOfTargetDOW += 7;
var resultedDay = firstDayOfTargetDOW + (7 * (Math.Abs(weekNumOfMonth) - 1));
if (resultedDay > daysOfMonth)
return null;
return (daysOfMonth - resultedDay);
}
}
usage
Assert.AreEqual(02, DateTimeHelper.GetNthDayOfWeekInMonth(2019, 11, DayOfWeek.Saturday, 1));
Assert.AreEqual(30, DateTimeHelper.GetNthDayOfWeekInMonth(2019, 11, DayOfWeek.Saturday, -1));
I know this post is old. I have this solution, trying to find a more clean code. #unclebob
public static DateTime FindTheNthDay(
int year, int month, DayOfWeek day, int occurrence)
{
var startDate = new DateTime(year, month, 1);
while(startDate.DayOfWeek != day)
{
startDate = startDate.AddDays(1);
}
var nDays = 7 * (occurrence - 1);
var result = startDate.AddDays(nDays);
return result;
}
> FindTheNthDay(2006, 11, DayOfWeek.Friday, 4)
[11/24/2006 12:00:00 AM]
> FindTheNthDay(2005, 11, DayOfWeek.Friday, 4)
[11/25/2005 12:00:00 AM]
> FindTheNthDay(2004, 11, DayOfWeek.Friday, 4)
[11/26/2004 12:00:00 AM]
> FindTheNthDay(2003, 11, DayOfWeek.Friday, 4)
[11/28/2003 12:00:00 AM]
> FindTheNthDay(1983, 11, DayOfWeek.Friday, 4)
[11/25/1983 12:00:00 AM]
> FindTheNthDay(1978, 11, DayOfWeek.Friday, 4)
[11/24/1978 12:00:00 AM]
> FindTheNthDay(1972, 11, DayOfWeek.Friday, 4)
[11/24/1972 12:00:00 AM]
public static bool IsThirdWednesday(DateTime inputDate)
{
DateTime firstDayOfMonth = new DateTime(inputDate.Year, inputDate.Month, 1);
DateTime firstDayOfNextMonth = firstDayOfMonth.AddMonths(1);
int wednesdayCount = 0;
while(firstDayOfMonth < firstDayOfNextMonth)
{
if (firstDayOfMonth.DayOfWeek == DayOfWeek.Wednesday)
wednesdayCount++;
if (wednesdayCount == 3)
{
if (inputDate == firstDayOfMonth)
return true;
else
return false;
}
firstDayOfMonth = firstDayOfMonth.AddDays(1);
}
return false;
}

Date difference in years using C# [duplicate]

This question already has answers here:
How do I calculate someone's age based on a DateTime type birthday?
(74 answers)
Closed 6 years ago.
How can I calculate date difference between two dates in years?
For example: (Datetime.Now.Today() - 11/03/2007) in years.
I have written an implementation that properly works with dates exactly one year apart.
However, it does not gracefully handle negative timespans, unlike the other algorithm. It also doesn't use its own date arithmetic, instead relying upon the standard library for that.
So without further ado, here is the code:
DateTime zeroTime = new DateTime(1, 1, 1);
DateTime a = new DateTime(2007, 1, 1);
DateTime b = new DateTime(2008, 1, 1);
TimeSpan span = b - a;
// Because we start at year 1 for the Gregorian
// calendar, we must subtract a year here.
int years = (zeroTime + span).Year - 1;
// 1, where my other algorithm resulted in 0.
Console.WriteLine("Yrs elapsed: " + years);
Use:
int Years(DateTime start, DateTime end)
{
return (end.Year - start.Year - 1) +
(((end.Month > start.Month) ||
((end.Month == start.Month) && (end.Day >= start.Day))) ? 1 : 0);
}
We had to code a check to establish if the difference between two dates, a start and end date was greater than 2 years.
Thanks to the tips above it was done as follows:
DateTime StartDate = Convert.ToDateTime("01/01/2012");
DateTime EndDate = Convert.ToDateTime("01/01/2014");
DateTime TwoYears = StartDate.AddYears(2);
if EndDate > TwoYears .....
If you need it for knowing someone's age for trivial reasons then Timespan is OK but if you need for calculating superannuation, long term deposits or anything else for financial, scientific or legal purposes then I'm afraid Timespan won't be accurate enough because Timespan assumes that every year has the same number of days, same # of hours and same # of seconds).
In reality the length of some years will vary (for different reasons that are outside the scope of this answer). To get around Timespan's limitation then you can mimic what Excel does which is:
public int GetDifferenceInYears(DateTime startDate, DateTime endDate)
{
//Excel documentation says "COMPLETE calendar years in between dates"
int years = endDate.Year - startDate.Year;
if (startDate.Month == endDate.Month &&// if the start month and the end month are the same
endDate.Day < startDate.Day// AND the end day is less than the start day
|| endDate.Month < startDate.Month)// OR if the end month is less than the start month
{
years--;
}
return years;
}
var totalYears =
(DateTime.Today - new DateTime(2007, 03, 11)).TotalDays
/ 365.2425;
Average days from Wikipedia/Leap_year.
int Age = new DateTime((DateTime.Now - BirthDateTime).Ticks).Year;
To calculate the elapsed years (age), the result will be minus one.
var timeSpan = DateTime.Now - birthDateTime;
int age = new DateTime(timeSpan.Ticks).Year - 1;
Here is a neat trick which lets the system deal with leap years automagically. It gives an accurate answer for all date combinations.
DateTime dt1 = new DateTime(1987, 9, 23, 13, 12, 12, 0);
DateTime dt2 = new DateTime(2007, 6, 15, 16, 25, 46, 0);
DateTime tmp = dt1;
int years = -1;
while (tmp < dt2)
{
years++;
tmp = tmp.AddYears(1);
}
Console.WriteLine("{0}", years);
It's unclear how you want to handle fractional years, but perhaps like this:
DateTime now = DateTime.Now;
DateTime origin = new DateTime(2007, 11, 3);
int calendar_years = now.Year - origin.Year;
int whole_years = calendar_years - ((now.AddYears(-calendar_years) >= origin)? 0: 1);
int another_method = calendar_years - ((now.Month - origin.Month) * 32 >= origin.Day - now.Day)? 0: 1);
I implemented an extension method to get the number of years between two dates, rounded by whole months.
/// <summary>
/// Gets the total number of years between two dates, rounded to whole months.
/// Examples:
/// 2011-12-14, 2012-12-15 returns 1.
/// 2011-12-14, 2012-12-14 returns 1.
/// 2011-12-14, 2012-12-13 returns 0,9167.
/// </summary>
/// <param name="start">
/// Stardate of time period
/// </param>
/// <param name="end">
/// Enddate of time period
/// </param>
/// <returns>
/// Total Years between the two days
/// </returns>
public static double DifferenceTotalYears(this DateTime start, DateTime end)
{
// Get difference in total months.
int months = ((end.Year - start.Year) * 12) + (end.Month - start.Month);
// substract 1 month if end month is not completed
if (end.Day < start.Day)
{
months--;
}
double totalyears = months / 12d;
return totalyears;
}
public string GetAgeText(DateTime birthDate)
{
const double ApproxDaysPerMonth = 30.4375;
const double ApproxDaysPerYear = 365.25;
int iDays = (DateTime.Now - birthDate).Days;
int iYear = (int)(iDays / ApproxDaysPerYear);
iDays -= (int)(iYear * ApproxDaysPerYear);
int iMonths = (int)(iDays / ApproxDaysPerMonth);
iDays -= (int)(iMonths * ApproxDaysPerMonth);
return string.Format("{0} år, {1} måneder, {2} dage", iYear, iMonths, iDays);
}
I found this at TimeSpan for years, months and days:
DateTime target_dob = THE_DOB;
DateTime true_age = DateTime.MinValue + ((TimeSpan)(DateTime.Now - target_dob )); // Minimum value as 1/1/1
int yr = true_age.Year - 1;
If you're dealing with months and years you need something that knows how many days each month has and which years are leap years.
Enter the Gregorian Calendar (and other culture-specific Calendar implementations).
While Calendar doesn't provide methods to directly calculate the difference between two points in time, it does have methods such as
DateTime AddWeeks(DateTime time, int weeks)
DateTime AddMonths(DateTime time, int months)
DateTime AddYears(DateTime time, int years)
DateTime musteriDogum = new DateTime(dogumYil, dogumAy, dogumGun);
int additionalDays = ((DateTime.Now.Year - dogumYil) / 4); //Count of the years with 366 days
int extraDays = additionalDays + ((DateTime.Now.Year % 4 == 0 || musteriDogum.Year % 4 == 0) ? 1 : 0); //We add 1 if this year or year inserted has 366 days
int yearsOld = ((DateTime.Now - musteriDogum).Days - extraDays ) / 365; // Now we extract these extra days from total days and we can divide to 365
Works perfect:
internal static int GetDifferenceInYears(DateTime startDate)
{
int finalResult = 0;
const int DaysInYear = 365;
DateTime endDate = DateTime.Now;
TimeSpan timeSpan = endDate - startDate;
if (timeSpan.TotalDays > 365)
{
finalResult = (int)Math.Round((timeSpan.TotalDays / DaysInYear), MidpointRounding.ToEven);
}
return finalResult;
}
Simple solution:
public int getYearDiff(DateTime startDate, DateTime endDate){
int y = Year(endDate) - Year(startDate);
int startMonth = Month(startDate);
int endMonth = Month(endDate);
if (endMonth < startMonth)
return y - 1;
if (endMonth > startMonth)
return y;
return (Day(endDate) < Day(startDate) ? y - 1 : y);
}
This is the best code to calculate year and month difference:
DateTime firstDate = DateTime.Parse("1/31/2019");
DateTime secondDate = DateTime.Parse("2/1/2016");
int totalYears = firstDate.Year - secondDate.Year;
int totalMonths = 0;
if (firstDate.Month > secondDate.Month)
totalMonths = firstDate.Month - secondDate.Month;
else if (firstDate.Month < secondDate.Month)
{
totalYears -= 1;
int monthDifference = secondDate.Month - firstDate.Month;
totalMonths = 12 - monthDifference;
}
if ((firstDate.Day - secondDate.Day) == 30)
{
totalMonths += 1;
if (totalMonths % 12 == 0)
{
totalYears += 1;
totalMonths = 0;
}
}
Maybe this will be helpful for answering the question: Count of days in given year,
new DateTime(anyDate.Year, 12, 31).DayOfYear //will include leap years too
Regarding DateTime.DayOfYear Property.
The following is based off Dana's simple code which produces the correct answer in most cases. But it did not take in to account less than a year between dates. So here is the code that I use to produce consistent results:
public static int DateDiffYears(DateTime startDate, DateTime endDate)
{
var yr = endDate.Year - startDate.Year - 1 +
(endDate.Month >= startDate.Month && endDate.Day >= startDate.Day ? 1 : 0);
return yr < 0 ? 0 : yr;
}

Get the number of calendar weeks between 2 dates in C#

For the purposes of this question, let's assume the user will be from the US and will use the standard Gregorian calendar. So, a calendar week starts on Sunday and ends on Saturday.
What I'm trying to do is determine the number of calendar weeks that exist between two dates. A perfect example of my problem exists in October 2010. Between 10/16 and 10/31 there are 4 calendar weeks.
October 10 - October 16
October 17 - October 23
October 24 - October 30
October 31 - November 6
I'd prefer to stay away from any hardcoded logic like:
if (Day == DayOfWeek.Saturday && LastDayOfMonth == 31) { ... }
Can anyone think of a logical way to do this?
UPDATE:
Thanks for all the great responses, after some consideration here is the solution I used:
//get the start and end dates of the current pay period
DateTime currentPeriodStart = SelectedPeriod.Model.PeriodStart;
DateTime currentPeriodEnd = SelectedPeriod.Model.PeriodEnd;
//get the first sunday & last saturday span that encapsulates the current pay period
DateTime firstSunday = DayExtensions.SundayBeforePeriodStart(currentPeriodStart);
DateTime lastSaturday = DayExtensions.SaturdayAfterPeriodEnd(currentPeriodEnd);
//get the number of calendar weeks in the span
int numberOfCalendarWeeks = DayExtensions.CalendarWeeks(firstSunday, lastSaturday);
And here are the methods from the helper class:
/// <summary>
/// Get the first Sunday before the pay period start date
/// </summary>
/// <param name="periodStartDate">Date of the pay period start date</param>
/// <returns></returns>
public static DateTime SundayBeforePeriodStart(DateTime periodStartDate)
{
DateTime firstDayOfWeekBeforeStartDate;
int daysBetweenStartDateAndPreviousFirstDayOfWeek = (int)periodStartDate.DayOfWeek - (int)DayOfWeek.Sunday;
if (daysBetweenStartDateAndPreviousFirstDayOfWeek >= 0)
{
firstDayOfWeekBeforeStartDate = periodStartDate.AddDays(-daysBetweenStartDateAndPreviousFirstDayOfWeek);
}
else
{
firstDayOfWeekBeforeStartDate = periodStartDate.AddDays(-(daysBetweenStartDateAndPreviousFirstDayOfWeek + 7));
}
return firstDayOfWeekBeforeStartDate;
}
/// <summary>
/// Get the first Saturday after the period end date
/// </summary>
/// <param name="periodEndDate">Date of the pay period end date</param>
/// <returns></returns>
public static DateTime SaturdayAfterPeriodEnd(DateTime periodEndDate)
{
DateTime lastDayOfWeekAfterEndDate;
int daysBetweenEndDateAndFollowingLastDayOfWeek = (int)DayOfWeek.Saturday - (int)periodEndDate.DayOfWeek;
if (daysBetweenEndDateAndFollowingLastDayOfWeek >= 0)
{
lastDayOfWeekAfterEndDate = periodEndDate.AddDays(daysBetweenEndDateAndFollowingLastDayOfWeek);
}
else
{
lastDayOfWeekAfterEndDate = periodEndDate.AddDays(daysBetweenEndDateAndFollowingLastDayOfWeek + 7);
}
return lastDayOfWeekAfterEndDate;
}
/// <summary>
/// Get the calendar weeks between 2 dates
/// </summary>
/// <param name="d1">First day of date span</param>
/// <param name="d2">Last day of date span</param>
/// <returns></returns>
public static int CalendarWeeks(DateTime d1, DateTime d2)
{
return 1 + (int)((d2 - d1).TotalDays / 7);
}
And if you're curious, this is what I end up doing with the dates:
//create an array of all the sundays in this span
DateTime[] _sundays = new DateTime[numberOfCalendarWeeks];
//put the first sunday in the period
_sundays[0] = firstSunday;
//step through each week and get each sunday until you reach the last saturday
for (int i = 1; i <= numberOfCalendarWeeks - 1; i++)
{
DateTime d = new DateTime();
d = firstSunday.AddDays(i * 7);
_sundays[i] = d;
}
for (int i = 0; i <= _sundays.Length-1; i++)
{
//bind my view model with each sunday.
}
Here's a general solution which I believe should work for any choice of week starting and ending days. You could simplify it for your case, but this code gives you the option of changing the week's start and end (e.g. to Monday to Sunday) if it becomes necessary. It's not uncommon in payroll applications for the definition of a calendar week to change.
DateTime periodStart = new DateTime(2010, 10, 17);
DateTime periodEnd = new DateTime(2010, 11, 14);
const DayOfWeek FIRST_DAY_OF_WEEK = DayOfWeek.Monday;
const DayOfWeek LAST_DAY_OF_WEEK = DayOfWeek.Sunday;
const int DAYS_IN_WEEK = 7;
DateTime firstDayOfWeekBeforeStartDate;
int daysBetweenStartDateAndPreviousFirstDayOfWeek = (int)periodStart.DayOfWeek - (int)FIRST_DAY_OF_WEEK;
if (daysBetweenStartDateAndPreviousFirstDayOfWeek >= 0)
{
firstDayOfWeekBeforeStartDate = periodStart.AddDays(-daysBetweenStartDateAndPreviousFirstDayOfWeek);
}
else
{
firstDayOfWeekBeforeStartDate = periodStart.AddDays(-(daysBetweenStartDateAndPreviousFirstDayOfWeek + DAYS_IN_WEEK));
}
DateTime lastDayOfWeekAfterEndDate;
int daysBetweenEndDateAndFollowingLastDayOfWeek = (int)LAST_DAY_OF_WEEK - (int)periodEnd.DayOfWeek;
if (daysBetweenEndDateAndFollowingLastDayOfWeek >= 0)
{
lastDayOfWeekAfterEndDate = periodEnd.AddDays(daysBetweenEndDateAndFollowingLastDayOfWeek);
}
else
{
lastDayOfWeekAfterEndDate = periodEnd.AddDays(daysBetweenEndDateAndFollowingLastDayOfWeek + DAYS_IN_WEEK);
}
int calendarWeeks = 1 + (int)((lastDayOfWeekAfterEndDate - firstDayOfWeekBeforeStartDate).TotalDays / DAYS_IN_WEEK);
The way I would tackle this is to get the beginning of the week for any given date. Using that, you would subtract the result of the start date from the result of the end date. Your day difference would then always be a multiple of 7, so divide and add 1 (0 days => 1 week, 7 days => 2 weeks, etc.). This would tell you how many calendar weeks were covered or represented by any two dates.
static int GetWeeksCovered(DateTime startDate, DateTime endDate)
{
if (endDate < startDate)
throw new ArgumentException("endDate cannot be less than startDate");
return (GetBeginningOfWeek(endDate).Subtract(GetBeginningOfWeek(startDate)).Days / 7) + 1;
}
static DateTime GetBeginningOfWeek(DateTime date)
{
return date.AddDays(-1 * (int)date.DayOfWeek).Date;
}
16-Oct-2010 and 16-Oct-2010 => 1 week covered (or represented).
16-Oct-2010 and 31-Oct-2010 => 4 weeks covered, as per the spec.
Bear in mind, that week calculations are done differently in different cultures and there is not a bug if you see week number 53!
using System.Globalization;
CultureInfo cultInfo = CultureInfo.CurrentCulture;
int weekNumNow = cultInfo.Calendar.GetWeekOfYear(DateTime.Now,
cultInfo.DateTimeFormat.CalendarWeekRule,
cultInfo.DateTimeFormat.FirstDayOfWeek);
private static int weekDifference(DateTime startDate, DateTime endDate)
{
int monthsApart = 12 * (startDate.Year - endDate.Year) + startDate.Month - endDate.Month;
return Math.Abs(monthsApart*4);
}
Cant See a better way right off the bat.
Saturday is the last day of the week huh?
public int CalendarWeeks(DateTime from, DateTime to) {
// number of multiples of 7
// (rounded up, since 15 days would span at least 3 weeks)
// and if we end on a day before we start, we know it's another week
return (int)Math.Ceiling(to.Subtract(from).Days / 7.0) +
(to.DayOfWeek <= from.DayOfWeek) ? 1 : 0;
}
The following seems to work for any date range. It should be culturally sound, and should account for leap years/days or other calendar oddities:
private static int getWeeksSpannedBy(DateTime first, DateTime last)
{
var calendar = CultureInfo.CurrentCulture.Calendar;
var weekRule = CultureInfo.CurrentCulture.DateTimeFormat.CalendarWeekRule;
var firstDayOfWeek = DayOfWeek.Sunday;
int lastWeek = calendar.GetWeekOfYear(last, weekRule, firstDayOfWeek);
int firstWeek = calendar.GetWeekOfYear(first, weekRule, firstDayOfWeek);
int weekDiff = lastWeek - firstWeek + 1;
return weekDiff;
}
static void Main(string[] args)
{
int weeks1 = getWeeksSpannedBy(new DateTime(2010, 1, 3), new DateTime(2010, 1, 9));
int weeks2 = getWeeksSpannedBy(new DateTime(2010, 10, 16), new DateTime(2010, 10, 31));
int weeks3 = getWeeksSpannedBy(new DateTime(2008, 2, 1), new DateTime(2008, 2, 29));
int weeks4 = getWeeksSpannedBy(new DateTime(2012, 2, 1), new DateTime(2012, 2, 29));
Console.WriteLine("Weeks Difference #1: " + weeks1);
Console.WriteLine("Weeks Difference #2: " + weeks2);
Console.WriteLine("Weeks Difference #3: " + weeks3);
Console.WriteLine("Weeks Difference #4: " + weeks4);
Console.ReadLine();
}
Prints out the following, which is correct for all date ranges, past, present, or future (leap year 2008 and 2012 both have 5 weeks between Feb 1 and Feb 29):
Weeks Difference #1: 1
Weeks Difference #2: 4
Weeks Difference #3: 5
Weeks Difference #4: 5
private static int weekDifference(DateTime startDate, DateTime endDate)
{
const int firstDayOfWeek = 0; // Sunday
int wasteDaysStart = (7+startDate.DatOfWeek-firstDayOfWeek)%7;
return (int)(((endDate-startDate).TotalDays() + wasteDaysStart + 6)/7);
}
Warning: untested code. Please test and remove note.

Calculate the number of weekdays between two dates in C#

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 but I can't seem to find a conclusive correct answer that abides by the following:
The total should be inclusive, so GetNumberOfWeekdays(new DateTime(2009,11,30), new DateTime(2009,12,4)) should equal 5, that's Monday to Friday.
Should allow for leap days
does NOT just iterate through all the dates between whilst counting the weekdays.
I've found a similar question with an answer that comes close but is not correct
O(1) solution:
// Count days from d0 to d1 inclusive, excluding weekends
public static int countWeekDays(DateTime d0, DateTime d1)
{
int ndays = 1 + Convert.ToInt32((d1 - d0).TotalDays);
int nsaturdays = (ndays + Convert.ToInt32(d0.DayOfWeek)) / 7;
return ndays - 2 * nsaturdays
- (d0.DayOfWeek == DayOfWeek.Sunday ? 1 : 0)
+ (d1.DayOfWeek == DayOfWeek.Saturday ? 1 : 0);
}
Examples for January 2014:
January 2014
Su Mo Tu We Th Fr Sa
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 1)); // 1
countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 2)); // 2
countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 3)); // 3
countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 4)); // 3
countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 5)); // 3
countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 6)); // 4
N.B. The DateTime inputs should be at around the same time of the day. If you are creating DateTime objects based solely on year, month, and day as in the examples above, then you should be fine. As a counter example, 12:01am on Jan 1 to 11:59pm Jan 2 spans only 2 days, but the above function will count 3 if you use those times.
From this link:
public static int Weekdays(DateTime dtmStart, DateTime dtmEnd)
{
// This function includes the start and end date in the count if they fall on a weekday
int dowStart = ((int)dtmStart.DayOfWeek == 0 ? 7 : (int)dtmStart.DayOfWeek);
int dowEnd = ((int)dtmEnd.DayOfWeek == 0 ? 7 : (int)dtmEnd.DayOfWeek);
TimeSpan tSpan = dtmEnd - dtmStart;
if (dowStart <= dowEnd)
{
return (((tSpan.Days / 7) * 5) + Math.Max((Math.Min((dowEnd + 1), 6) - dowStart), 0));
}
return (((tSpan.Days / 7) * 5) + Math.Min((dowEnd + 6) - Math.Min(dowStart, 6), 5));
}
[1]: http://www.eggheadcafe.com/community/aspnet/2/44982/how-to-calculate-num-of-w.aspx
Tests (each test returns 5):
int ndays = Weekdays(new DateTime(2009, 11, 30), new DateTime(2009, 12, 4));
System.Console.WriteLine(ndays);
// leap year test
ndays = Weekdays(new DateTime(2000, 2,27), new DateTime(2000, 3, 5));
System.Console.WriteLine(ndays);
// non leap year test
ndays = Weekdays(new DateTime(2007, 2, 25), new DateTime(2007, 3, 4));
System.Console.WriteLine(ndays);
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;
}
This should do better than the solution by dcp:
/// <summary>
/// Count Weekdays between two dates
/// </summary>
/// <param name="dtmStart">first date</param>
/// <param name="dtmEnd">second date</param>
/// <returns>weekdays between the two dates, including the start and end day</returns>
internal static int getWeekdaysBetween(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;
return weekDays;
}
I needed positive / negatives (not absolute values) so here's how I solved it:
public static int WeekdayDifference(DateTime StartDate, DateTime EndDate)
{
DateTime thisDate = StartDate;
int weekDays = 0;
while (thisDate != EndDate)
{
if (thisDate.DayOfWeek != DayOfWeek.Saturday && thisDate.DayOfWeek != DayOfWeek.Sunday) { weekDays++; }
if (EndDate > StartDate) { thisDate = thisDate.AddDays(1); } else { thisDate = thisDate.AddDays(-1); }
}
/* Determine if value is positive or negative */
if (EndDate > StartDate) {
return weekDays;
}
else
{
return weekDays * -1;
}
}
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);
}
public static List<DateTime> Weekdays(DateTime startDate, DateTime endDate)
{
if (startDate > endDate)
{
Swap(ref startDate, ref endDate);
}
List<DateTime> days = new List<DateTime>();
var ts = endDate - startDate;
for (int i = 0; i < ts.TotalDays; i++)
{
var cur = startDate.AddDays(i);
if (cur.DayOfWeek != DayOfWeek.Saturday && cur.DayOfWeek != DayOfWeek.Sunday)
days.Add(cur);
//if (startingDate.AddDays(i).DayOfWeek != DayOfWeek.Saturday || startingDate.AddDays(i).DayOfWeek != DayOfWeek.Sunday)
//yield return startingDate.AddDays(i);
}
return days;
}
And swap dates
private static void Swap(ref DateTime startDate, ref DateTime endDate)
{
object a = startDate;
startDate = endDate;
endDate = (DateTime)a;
}
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");
var dates = new List<DateTime>();
for (var dt = YourStartDate; dt <= YourEndDate; dt = dt.AddDays(1))
{
if (dt.DayOfWeek != DayOfWeek.Sunday && dt.DayOfWeek != DayOfWeek.Saturday)
{ dates.Add(dt); }
}
in this code you could have a list that all buisness days between two dates.
if you want the count of these dates you could get dates.Count as an integer.
or if you want to get the each day , you could join the list to a string.
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);
}
}
}
}
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;
}

AddBusinessDays and GetBusinessDays

I need to find 2 elegant complete implementations of
public static DateTime AddBusinessDays(this DateTime date, int days)
{
// code here
}
and
public static int GetBusinessDays(this DateTime start, DateTime end)
{
// code here
}
O(1) preferable (no loops).
EDIT:
By business days i mean working days (Monday, Tuesday, Wednesday, Thursday, Friday). No holidays, just weekends excluded.
I already have some ugly solutions that seem to work but i wonder if there are elegant ways to do this. Thanks
This is what i've written so far. It works in all cases and does negatives too.
Still need a GetBusinessDays implementation
public static DateTime AddBusinessDays(this DateTime startDate,
int businessDays)
{
int direction = Math.Sign(businessDays);
if(direction == 1)
{
if(startDate.DayOfWeek == DayOfWeek.Saturday)
{
startDate = startDate.AddDays(2);
businessDays = businessDays - 1;
}
else if(startDate.DayOfWeek == DayOfWeek.Sunday)
{
startDate = startDate.AddDays(1);
businessDays = businessDays - 1;
}
}
else
{
if(startDate.DayOfWeek == DayOfWeek.Saturday)
{
startDate = startDate.AddDays(-1);
businessDays = businessDays + 1;
}
else if(startDate.DayOfWeek == DayOfWeek.Sunday)
{
startDate = startDate.AddDays(-2);
businessDays = businessDays + 1;
}
}
int initialDayOfWeek = (int)startDate.DayOfWeek;
int weeksBase = Math.Abs(businessDays / 5);
int addDays = Math.Abs(businessDays % 5);
if((direction == 1 && addDays + initialDayOfWeek > 5) ||
(direction == -1 && addDays >= initialDayOfWeek))
{
addDays += 2;
}
int totalDays = (weeksBase * 7) + addDays;
return startDate.AddDays(totalDays * direction);
}
Latest attempt for your first function:
public static DateTime AddBusinessDays(DateTime date, int days)
{
if (days < 0)
{
throw new ArgumentException("days cannot be negative", "days");
}
if (days == 0) return date;
if (date.DayOfWeek == DayOfWeek.Saturday)
{
date = date.AddDays(2);
days -= 1;
}
else if (date.DayOfWeek == DayOfWeek.Sunday)
{
date = date.AddDays(1);
days -= 1;
}
date = date.AddDays(days / 5 * 7);
int extraDays = days % 5;
if ((int)date.DayOfWeek + extraDays > 5)
{
extraDays += 2;
}
return date.AddDays(extraDays);
}
The second function, GetBusinessDays, can be implemented as follows:
public static int GetBusinessDays(DateTime start, DateTime end)
{
if (start.DayOfWeek == DayOfWeek.Saturday)
{
start = start.AddDays(2);
}
else if (start.DayOfWeek == DayOfWeek.Sunday)
{
start = start.AddDays(1);
}
if (end.DayOfWeek == DayOfWeek.Saturday)
{
end = end.AddDays(-1);
}
else if (end.DayOfWeek == DayOfWeek.Sunday)
{
end = end.AddDays(-2);
}
int diff = (int)end.Subtract(start).TotalDays;
int result = diff / 7 * 5 + diff % 7;
if (end.DayOfWeek < start.DayOfWeek)
{
return result - 2;
}
else{
return result;
}
}
using Fluent DateTime:
var now = DateTime.Now;
var dateTime1 = now.AddBusinessDays(3);
var dateTime2 = now.SubtractBusinessDays(5);
internal code is as follows
/// <summary>
/// Adds the given number of business days to the <see cref="DateTime"/>.
/// </summary>
/// <param name="current">The date to be changed.</param>
/// <param name="days">Number of business days to be added.</param>
/// <returns>A <see cref="DateTime"/> increased by a given number of business days.</returns>
public static DateTime AddBusinessDays(this DateTime current, int days)
{
var sign = Math.Sign(days);
var unsignedDays = Math.Abs(days);
for (var i = 0; i < unsignedDays; i++)
{
do
{
current = current.AddDays(sign);
}
while (current.DayOfWeek == DayOfWeek.Saturday ||
current.DayOfWeek == DayOfWeek.Sunday);
}
return current;
}
/// <summary>
/// Subtracts the given number of business days to the <see cref="DateTime"/>.
/// </summary>
/// <param name="current">The date to be changed.</param>
/// <param name="days">Number of business days to be subtracted.</param>
/// <returns>A <see cref="DateTime"/> increased by a given number of business days.</returns>
public static DateTime SubtractBusinessDays(this DateTime current, int days)
{
return AddBusinessDays(current, -days);
}
I created an extension that allows you to add or subtract business days.
Use a negative number of businessDays to subtract. I think it's quite an elegant solution. It seems to work in all cases.
namespace Extensions.DateTime
{
public static class BusinessDays
{
public static System.DateTime AddBusinessDays(this System.DateTime source, int businessDays)
{
var dayOfWeek = businessDays < 0
? ((int)source.DayOfWeek - 12) % 7
: ((int)source.DayOfWeek + 6) % 7;
switch (dayOfWeek)
{
case 6:
businessDays--;
break;
case -6:
businessDays++;
break;
}
return source.AddDays(businessDays + ((businessDays + dayOfWeek) / 5) * 2);
}
}
}
Example:
using System;
using System.Windows.Forms;
using Extensions.DateTime;
namespace AddBusinessDaysTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
label1.Text = DateTime.Now.AddBusinessDays(5).ToString();
label2.Text = DateTime.Now.AddBusinessDays(-36).ToString();
}
}
}
For me I had to have a solution that would skip weekends and go in either negative or positive. My criteria was if it went forward and landed on a weekend it would need to advance to Monday. If it was going back and landed on a weekend it would have to jump to Friday.
For example:
Wednesday - 3 business days = Last Friday
Wednesday + 3 business days = Monday
Friday - 7 business days = Last Wednesday
Tuesday - 5 business days = Last Tuesday
Well you get the idea ;)
I ended up writing this extension class
public static partial class MyExtensions
{
public static DateTime AddBusinessDays(this DateTime date, int addDays)
{
while (addDays != 0)
{
date = date.AddDays(Math.Sign(addDays));
if (MyClass.IsBusinessDay(date))
{
addDays = addDays - Math.Sign(addDays);
}
}
return date;
}
}
It uses this method I thought would be useful to use elsewhere...
public class MyClass
{
public static bool IsBusinessDay(DateTime date)
{
switch (date.DayOfWeek)
{
case DayOfWeek.Monday:
case DayOfWeek.Tuesday:
case DayOfWeek.Wednesday:
case DayOfWeek.Thursday:
case DayOfWeek.Friday:
return true;
default:
return false;
}
}
}
If you don't want to bother with that you can just replace out if (MyClass.IsBusinessDay(date)) with if if ((date.DayOfWeek != DayOfWeek.Saturday) && (date.DayOfWeek != DayOfWeek.Sunday))
So now you can do
var myDate = DateTime.Now.AddBusinessDays(-3);
or
var myDate = DateTime.Now.AddBusinessDays(5);
Here are the results from some testing:
Test Expected Result
Wednesday -4 business days Thursday Thursday
Wednesday -3 business days Friday Friday
Wednesday +3 business days Monday Monday
Friday -7 business days Wednesday Wednesday
Tuesday -5 business days Tuesday Tuesday
Friday +1 business days Monday Monday
Saturday +1 business days Monday Monday
Sunday -1 business days Friday Friday
Monday -1 business days Friday Friday
Monday +1 business days Tuesday Tuesday
Monday +0 business days Monday Monday
public static DateTime AddBusinessDays(this DateTime date, int days)
{
date = date.AddDays((days / 5) * 7);
int remainder = days % 5;
switch (date.DayOfWeek)
{
case DayOfWeek.Tuesday:
if (remainder > 3) date = date.AddDays(2);
break;
case DayOfWeek.Wednesday:
if (remainder > 2) date = date.AddDays(2);
break;
case DayOfWeek.Thursday:
if (remainder > 1) date = date.AddDays(2);
break;
case DayOfWeek.Friday:
if (remainder > 0) date = date.AddDays(2);
break;
case DayOfWeek.Saturday:
if (days > 0) date = date.AddDays((remainder == 0) ? 2 : 1);
break;
case DayOfWeek.Sunday:
if (days > 0) date = date.AddDays((remainder == 0) ? 1 : 0);
break;
default: // monday
break;
}
return date.AddDays(remainder);
}
I'm coming late for the answer, but I made a little library with all the customization needed to do simple operations on working days... I leave it here : Working Days Management
The only real solution is to have those calls access a database table that defines the calendar for your business. You could code it for a Monday to Friday workweek without too much difficult but handling holidays would be a challenge.
Edited to add non-elegant and non-tested partial solution:
public static DateTime AddBusinessDays(this DateTime date, int days)
{
for (int index = 0; index < days; index++)
{
switch (date.DayOfWeek)
{
case DayOfWeek.Friday:
date = date.AddDays(3);
break;
case DayOfWeek.Saturday:
date = date.AddDays(2);
break;
default:
date = date.AddDays(1);
break;
}
}
return date;
}
Also I violated the no loops requirement.
I am resurrecting this post because today I had to find a way to exclude not only Saturday and Sunday weekdays but also holidays. More specifically, I needed to handle various sets of possible holidays, including:
country-invariant holidays (at least for the Western countries - such as January, 01).
calculated holidays (such as Easter and Easter monday).
country-specific holidays (such as the Italian liberation day or the United States ID4).
town-specific holidays (such as the Rome St. Patron Day).
any other custom-made holiday (such as "tomorrow our office wil be closed").
Eventually, I came out with the following set of helper/extensions classes: although they aren't blatantly elegant, as they do make a massive use of unefficient loops, they are decent enough to solve my issues for good. I'm dropping the whole source code here in this post, hoping it will be useful to someone else as well.
Source code
/// <summary>
/// Helper/extension class for manipulating date and time values.
/// </summary>
public static class DateTimeExtensions
{
/// <summary>
/// Calculates the absolute year difference between two dates.
/// </summary>
/// <param name="dt1"></param>
/// <param name="dt2"></param>
/// <returns>A whole number representing the number of full years between the specified dates.</returns>
public static int Years(DateTime dt1,DateTime dt2)
{
return Months(dt1,dt2)/12;
//if (dt2<dt1)
//{
// DateTime dt0=dt1;
// dt1=dt2;
// dt2=dt0;
//}
//int diff=dt2.Year-dt1.Year;
//int m1=dt1.Month;
//int m2=dt2.Month;
//if (m2>m1) return diff;
//if (m2==m1 && dt2.Day>=dt1.Day) return diff;
//return (diff-1);
}
/// <summary>
/// Calculates the absolute year difference between two dates.
/// Alternative, stand-alone version (without other DateTimeUtil dependency nesting required)
/// </summary>
/// <param name="start"></param>
/// <param name="end"></param>
/// <returns></returns>
public static int Years2(DateTime start, DateTime end)
{
return (end.Year - start.Year - 1) +
(((end.Month > start.Month) ||
((end.Month == start.Month) && (end.Day >= start.Day))) ? 1 : 0);
}
/// <summary>
/// Calculates the absolute month difference between two dates.
/// </summary>
/// <param name="dt1"></param>
/// <param name="dt2"></param>
/// <returns>A whole number representing the number of full months between the specified dates.</returns>
public static int Months(DateTime dt1,DateTime dt2)
{
if (dt2<dt1)
{
DateTime dt0=dt1;
dt1=dt2;
dt2=dt0;
}
dt2=dt2.AddDays(-(dt1.Day-1));
return (dt2.Year-dt1.Year)*12+(dt2.Month-dt1.Month);
}
/// <summary>
/// Returns the higher of the two date time values.
/// </summary>
/// <param name="dt1">The first of the two <c>DateTime</c> values to compare.</param>
/// <param name="dt2">The second of the two <c>DateTime</c> values to compare.</param>
/// <returns><c>dt1</c> or <c>dt2</c>, whichever is higher.</returns>
public static DateTime Max(DateTime dt1,DateTime dt2)
{
return (dt2>dt1?dt2:dt1);
}
/// <summary>
/// Returns the lower of the two date time values.
/// </summary>
/// <param name="dt1">The first of the two <c>DateTime</c> values to compare.</param>
/// <param name="dt2">The second of the two <c>DateTime</c> values to compare.</param>
/// <returns><c>dt1</c> or <c>dt2</c>, whichever is lower.</returns>
public static DateTime Min(DateTime dt1,DateTime dt2)
{
return (dt2<dt1?dt2:dt1);
}
/// <summary>
/// Adds the given number of business days to the <see cref="DateTime"/>.
/// </summary>
/// <param name="current">The date to be changed.</param>
/// <param name="days">Number of business days to be added.</param>
/// <param name="holidays">An optional list of holiday (non-business) days to consider.</param>
/// <returns>A <see cref="DateTime"/> increased by a given number of business days.</returns>
public static DateTime AddBusinessDays(
this DateTime current,
int days,
IEnumerable<DateTime> holidays = null)
{
var sign = Math.Sign(days);
var unsignedDays = Math.Abs(days);
for (var i = 0; i < unsignedDays; i++)
{
do
{
current = current.AddDays(sign);
}
while (current.DayOfWeek == DayOfWeek.Saturday
|| current.DayOfWeek == DayOfWeek.Sunday
|| (holidays != null && holidays.Contains(current.Date))
);
}
return current;
}
/// <summary>
/// Subtracts the given number of business days to the <see cref="DateTime"/>.
/// </summary>
/// <param name="current">The date to be changed.</param>
/// <param name="days">Number of business days to be subtracted.</param>
/// <param name="holidays">An optional list of holiday (non-business) days to consider.</param>
/// <returns>A <see cref="DateTime"/> increased by a given number of business days.</returns>
public static DateTime SubtractBusinessDays(
this DateTime current,
int days,
IEnumerable<DateTime> holidays)
{
return AddBusinessDays(current, -days, holidays);
}
/// <summary>
/// Retrieves the number of business days from two dates
/// </summary>
/// <param name="startDate">The inclusive start date</param>
/// <param name="endDate">The inclusive end date</param>
/// <param name="holidays">An optional list of holiday (non-business) days to consider.</param>
/// <returns></returns>
public static int GetBusinessDays(
this DateTime startDate,
DateTime endDate,
IEnumerable<DateTime> holidays)
{
if (startDate > endDate)
throw new NotSupportedException("ERROR: [startDate] cannot be greater than [endDate].");
int cnt = 0;
for (var current = startDate; current < endDate; current = current.AddDays(1))
{
if (current.DayOfWeek == DayOfWeek.Saturday
|| current.DayOfWeek == DayOfWeek.Sunday
|| (holidays != null && holidays.Contains(current.Date))
)
{
// skip holiday
}
else cnt++;
}
return cnt;
}
/// <summary>
/// Calculate Easter Sunday for any given year.
/// src.: https://stackoverflow.com/a/2510411/1233379
/// </summary>
/// <param name="year">The year to calcolate Easter against.</param>
/// <returns>a DateTime object containing the Easter month and day for the given year</returns>
public static DateTime GetEasterSunday(int year)
{
int day = 0;
int month = 0;
int g = year % 19;
int c = year / 100;
int h = (c - (int)(c / 4) - (int)((8 * c + 13) / 25) + 19 * g + 15) % 30;
int i = h - (int)(h / 28) * (1 - (int)(h / 28) * (int)(29 / (h + 1)) * (int)((21 - g) / 11));
day = i - ((year + (int)(year / 4) + i + 2 - c + (int)(c / 4)) % 7) + 28;
month = 3;
if (day > 31)
{
month++;
day -= 31;
}
return new DateTime(year, month, day);
}
/// <summary>
/// Retrieve holidays for given years
/// </summary>
/// <param name="years">an array of years to retrieve the holidays</param>
/// <param name="countryCode">a country two letter ISO (ex.: "IT") to add the holidays specific for that country</param>
/// <param name="cityName">a city name to add the holidays specific for that city</param>
/// <returns></returns>
public static IEnumerable<DateTime> GetHolidays(IEnumerable<int> years, string countryCode = null, string cityName = null)
{
var lst = new List<DateTime>();
foreach (var year in years.Distinct())
{
lst.AddRange(new[] {
new DateTime(year, 1, 1), // 1 gennaio (capodanno)
new DateTime(year, 1, 6), // 6 gennaio (epifania)
new DateTime(year, 5, 1), // 1 maggio (lavoro)
new DateTime(year, 8, 15), // 15 agosto (ferragosto)
new DateTime(year, 11, 1), // 1 novembre (ognissanti)
new DateTime(year, 12, 8), // 8 dicembre (immacolata concezione)
new DateTime(year, 12, 25), // 25 dicembre (natale)
new DateTime(year, 12, 26) // 26 dicembre (s. stefano)
});
// add easter sunday (pasqua) and monday (pasquetta)
var easterDate = GetEasterSunday(year);
lst.Add(easterDate);
lst.Add(easterDate.AddDays(1));
// country-specific holidays
if (!String.IsNullOrEmpty(countryCode))
{
switch (countryCode.ToUpper())
{
case "IT":
lst.Add(new DateTime(year, 4, 25)); // 25 aprile (liberazione)
break;
case "US":
lst.Add(new DateTime(year, 7, 4)); // 4 luglio (Independence Day)
break;
// todo: add other countries
case default:
// unsupported country: do nothing
break;
}
}
// city-specific holidays
if (!String.IsNullOrEmpty(cityName))
{
switch (cityName)
{
case "Rome":
case "Roma":
lst.Add(new DateTime(year, 6, 29)); // 29 giugno (s. pietro e paolo)
break;
case "Milano":
case "Milan":
lst.Add(new DateTime(year, 12, 7)); // 7 dicembre (s. ambrogio)
break;
// todo: add other cities
default:
// unsupported city: do nothing
break;
}
}
}
return lst;
}
}
Usage info
The code is quite self-explanatory, however here's a couple examples to explain how you can use it.
Add 10 business days (skipping only saturday and sunday week days)
var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10);
Add 10 business days (skipping saturday, sunday and all country-invariant holidays for 2019)
var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10, GetHolidays(2019));
Add 10 business days (skipping saturday, sunday and all italian holidays for 2019)
var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10, GetHolidays(2019, "IT"));
Add 10 business days (skipping saturday, sunday, all italian holidays and the Rome-specific holidays for 2019)
var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10, GetHolidays(2019, "IT", "Rome"));
The above functions and code examples are further explained in this post of my blog.
Ok so this solution is slightly different (has some good and bad points):
Solves weekends depending on the country (i.e. Arab countries have Friday-Saturday weekends)
Depends on external library https://www.nuget.org/packages/Nager.Date/
Only deals with adding business days
Skips holidays as well (this can be removed)
Uses recursion
public static class DateTimeExtensions
{
public static DateTime AddBusinessDays(this DateTime date, int days, CountryCode countryCode)
{
if (days < 0)
{
throw new ArgumentException("days cannot be negative", "days");
}
if (days == 0)
{
return date;
}
date = date.AddDays(1);
if (DateSystem.IsWeekend(date, countryCode) || DateSystem.IsPublicHoliday(date, countryCode))
{
return date.AddBusinessDays(days, countryCode);
}
days -= 1;
return date.AddBusinessDays(days, countryCode);
}
}
Usage:
[TestFixture]
public class BusinessDaysTests
{
[TestCase("2021-06-04", 5, "2021-06-11", Nager.Date.CountryCode.GB)]
[TestCase("2021-06-04", 6, "2021-06-14", Nager.Date.CountryCode.GB)]
[TestCase("2021-05-28", 6, "2021-06-08", Nager.Date.CountryCode.GB)] // UK holidays 2021-05-31
[TestCase("2021-06-01", 3, "2021-06-06", Nager.Date.CountryCode.KW)] // Friday-Saturday weekend in Kuwait
public void AddTests(DateTime initDate, int delayDays, DateTime expectedDate, Nager.Date.CountryCode countryCode)
{
var outputDate = initDate.AddBusinessDays(delayDays, countryCode);
Assert.AreEqual(expectedDate, outputDate);
}
}
public static DateTime AddBusinessDays(DateTime date, int days)
{
if (days == 0) return date;
int i = 0;
while (i < days)
{
if (!(date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday)) i++;
date = date.AddDays(1);
}
return date;
}
I wanted an "AddBusinessDays" that supported negative numbers of days to add, and I ended up with this:
// 0 == Monday, 6 == Sunday
private static int epochDayToDayOfWeek0Based(long epochDay) {
return (int)Math.floorMod(epochDay + 3, 7);
}
public static int daysBetween(long fromEpochDay, long toEpochDay) {
// http://stackoverflow.com/questions/1617049/calculate-the-number-of-business-days-between-two-dates
final int fromDOW = epochDayToDayOfWeek0Based(fromEpochDay);
final int toDOW = epochDayToDayOfWeek0Based(toEpochDay);
long calcBusinessDays = ((toEpochDay - fromEpochDay) * 5 + (toDOW - fromDOW) * 2) / 7;
if (toDOW == 6) calcBusinessDays -= 1;
if (fromDOW == 6) calcBusinessDays += 1;
return (int)calcBusinessDays;
}
public static long addDays(long epochDay, int n) {
// https://alecpojidaev.wordpress.com/2009/10/29/work-days-calculation-with-c/
// NB: in .NET, Sunday == 0, but in our code Monday == 0
final int dow = (epochDayToDayOfWeek0Based(epochDay) + 1) % 7;
final int wds = n + (dow == 0 ? 1 : dow); // Adjusted number of working days to add, given that we now start from the immediately preceding Sunday
final int wends = n < 0 ? ((wds - 5) / 5) * 2
: (wds / 5) * 2 - (wds % 5 == 0 ? 2 : 0);
return epochDay - dow + // Find the immediately preceding Sunday
wds + // Add computed working days
wends; // Add weekends that occur within each complete working week
}
No loop required, so it should be reasonably fast even for "big" additions.
It works with days expressed as a number of calendar days since the epoch, since that's exposed by the new JDK8 LocalDate class and I was working in Java. Should be simple to adapt to other settings though.
The fundamental properties are that addDays always returns a weekday, and that for all d and n, daysBetween(d, addDays(d, n)) == n
Note that theoretically speaking adding 0 days and subtracting 0 days should be different operations (if your date is a Sunday, adding 0 days should take you to Monday, and subtracting 0 days should take you to Friday). Since there's no such thing as negative 0 (outside of floating point!), I've chosen to interpret an argument n=0 as meaning add zero days.
I believe this could be a simpler way to GetBusinessDays:
public int GetBusinessDays(DateTime start, DateTime end, params DateTime[] bankHolidays)
{
int tld = (int)((end - start).TotalDays) + 1; //including end day
int not_buss_day = 2 * (tld / 7); //Saturday and Sunday
int rest = tld % 7; //rest.
if (rest > 0)
{
int tmp = (int)start.DayOfWeek - 1 + rest;
if (tmp == 6 || start.DayOfWeek == DayOfWeek.Sunday) not_buss_day++; else if (tmp > 6) not_buss_day += 2;
}
foreach (DateTime bankHoliday in bankHolidays)
{
DateTime bh = bankHoliday.Date;
if (!(bh.DayOfWeek == DayOfWeek.Saturday || bh.DayOfWeek == DayOfWeek.Sunday) && (start <= bh && bh <= end))
{
not_buss_day++;
}
}
return tld - not_buss_day;
}
public static DateTime AddWorkingDays(this DateTime date, int daysToAdd)
{
while (daysToAdd > 0)
{
date = date.AddDays(1);
if (date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday)
{
daysToAdd -= 1;
}
}
return date;
}
public static int GetBusinessDays(this DateTime start, DateTime end)
{
return Enumerable.Range(0, (end- start).Days)
.Select(a => start.AddDays(a))
.Where(a => a.DayOfWeek != DayOfWeek.Sunday)
.Where(a => a.DayOfWeek != DayOfWeek.Saturday)
.Count();
}
Here is my code with both departure date, and delivery date at customer.
// Calculate departure date
TimeSpan DeliveryTime = new TimeSpan(14, 30, 0);
TimeSpan now = DateTime.Now.TimeOfDay;
DateTime dt = DateTime.Now;
if (dt.TimeOfDay > DeliveryTime) dt = dt.AddDays(1);
if (dt.DayOfWeek == DayOfWeek.Saturday) dt = dt.AddDays(1);
if (dt.DayOfWeek == DayOfWeek.Sunday) dt = dt.AddDays(1);
dt = dt.Date + DeliveryTime;
string DepartureDay = "today at "+dt.ToString("HH:mm");
if (dt.Day!=DateTime.Now.Day)
{
DepartureDay = dt.ToString("dddd at HH:mm", new CultureInfo(WebContextState.CurrentUICulture));
}
Return DepartureDay;
// Caclulate delivery date
dt = dt.AddDays(1);
if (dt.DayOfWeek == DayOfWeek.Saturday) dt = dt.AddDays(1);
if (dt.DayOfWeek == DayOfWeek.Sunday) dt = dt.AddDays(1);
string DeliveryDay = dt.ToString("dddd", new CultureInfo(WebContextState.CurrentUICulture));
return DeliveryDay;
You can use the Fluent DateTime library easily. Please refer to this Github library repository. https://github.com/FluentDateTime/FluentDateTime
var dateTime = DateTime.Now.AddBusinessDays(5);
You can consider weekdays as BusinessDays
Hope this helps someone.
private DateTime AddWorkingDays(DateTime addToDate, int numberofDays)
{
addToDate= addToDate.AddDays(numberofDays);
while (addToDate.DayOfWeek == DayOfWeek.Saturday || addToDate.DayOfWeek == DayOfWeek.Sunday)
{
addToDate= addToDate.AddDays(1);
}
return addToDate;
}

Categories