Get the number of calendar weeks between 2 dates in C# - 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.

Related

Calculating days left until the next specified day

In a scenario where you would need to calculate the next 'Billing date' if the DAY (2nd, 25th, etc) is known, how can you calculate the number of days left until the next bill payment?
Explanation:
Tom's bill gets generated on the 4th of every month
What's the best way/logic to calculate the days left until the next bill? For example, if today is the 28th of this month, the result would be 6 days left
What we know:
Bill Generation Date is known
Today's Date is known
What I've done so far:
int billingDay = 4; //The day the bill gets generated every month
DateTime today = DateTime.Today; //Today's date
How would I continue this to calculate the next billing date?
P.S: Sorry if this sounds lame, I just couldn't wrap my head around it :)
I think this works:
private int GetNumDaysToNextBillingDate(int billingDayOfMonth)
{
DateTime today = DateTime.Today;
if (today.Day <= billingDayOfMonth)
{
return (new DateTime(today.Year, today.Month, billingDayOfMonth) - today).Days;
}
else
{
var oneMonthFromToday = today.AddMonths(1);
var billingDateNextMonth =
new DateTime(oneMonthFromToday.Year,
oneMonthFromToday.Month, billingDayOfMonth);
return (billingDateNextMonth - today).Days;
}
}
How about:
int billingDay = 4;
DateTime today = DateTime.UtcNow;
DateTime billing = today.Day >= billingDay
? new DateTime(today.AddMonths(1).Year, today.AddMonths(1).Month, billingDay)
: new DateTime(today.Year, today.Month, billingDay);
TimeSpan left = billing - today;
This uses a loop but is less prone to error as it takes into account month and year changes:
int DaysUntilBilling(int billingDay, DateTime referenceDate)
{
int count = 0;
while (referenceDate.AddDays(count).Day != billingDay)
{
count++;
};
return count;
}
You of course don't need to pass a DateTime in as an argument if you are always using today's date, but this helps to test that that for different inputs, you get the desired output:
int billingDay = 4;
DaysUntilBilling(billingDay, DateTime.Now); //26 (today is 9th Aug 2016)
DaysUntilBilling(billingDay, new DateTime(2016, 09, 03); //1
DaysUntilBilling(billingDay, new DateTime(2016, 09, 04); //0
DaysUntilBilling(billingDay, new DateTime(2016, 08, 05); //30
DaysUntilBilling(billingDay, new DateTime(2016, 12, 19); //16
This link might help you :
https://msdn.microsoft.com/en-us/library/system.datetime.daysinmonth(v=vs.110).aspx
What you can do is something like this:
int daysUntilBill = 0;
int billingDay = 4;
DateTime today = DateTime.Today;
if (billingDay > today.Day) {
daysUntilBill = billingDay - today.Day;
} else {
int daysLeftInMonth = DateTime.DaysInMonth(today.Year, today.Month) - today.Day;
daysUntilBill = billingDay + daysLeftInMonth;
}
or slightly more concise
int daysUntilBill = (billingDay >= today.Day)
? billingDay - today.Day
: billingDay + DateTime.DaysInMonth(today.Year, today.Month) - today.Day;
This properly handles the year ending too, since it doesn't try to wrap around.
First you need to determine if the current date is on or before the billing day and if it is just subtract the current day of the month. Otherwise you have to determine the next billing date in the following month.
public int DaysToNextBill(int billingDay)
{
var today = DateTime.Today;
if(today.Day <= billingDay)
return billingDay - today.Day;
var nextMonth = today.AddMonth(1);
var nextBillingDate = new DateTime(nextMonth.Year, nextMonth.Month, billingDay)
return (nextBillingDate - today).Days;
}
The only thing left to deal with is if billingDay is greater than the number of days in the current or following month.

Compute the DateTime of an upcoming weekday

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;
}

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;
}

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;
}

How can I get the DateTime for the start of the week?

How do I find the start of the week (both Sunday and Monday) knowing just the current time in C#?
Something like:
DateTime.Now.StartWeek(Monday);
Use an extension method:
public static class DateTimeExtensions
{
public static DateTime StartOfWeek(this DateTime dt, DayOfWeek startOfWeek)
{
int diff = (7 + (dt.DayOfWeek - startOfWeek)) % 7;
return dt.AddDays(-1 * diff).Date;
}
}
Which can be used as follows:
DateTime dt = DateTime.Now.StartOfWeek(DayOfWeek.Monday);
DateTime dt = DateTime.Now.StartOfWeek(DayOfWeek.Sunday);
The quickest way I can come up with is:
var sunday = DateTime.Today.AddDays(-(int)DateTime.Today.DayOfWeek);
If you would like any other day of the week to be your start date, all you need to do is add the DayOfWeek value to the end
var monday = DateTime.Today.AddDays(-(int)DateTime.Today.DayOfWeek + (int)DayOfWeek.Monday);
var tuesday = DateTime.Today.AddDays(-(int)DateTime.Today.DayOfWeek + (int)DayOfWeek.Tuesday);
A little more verbose and culture-aware:
System.Globalization.CultureInfo ci =
System.Threading.Thread.CurrentThread.CurrentCulture;
DayOfWeek fdow = ci.DateTimeFormat.FirstDayOfWeek;
DayOfWeek today = DateTime.Now.DayOfWeek;
DateTime sow = DateTime.Now.AddDays(-(today - fdow)).Date;
Using Fluent DateTime:
var monday = DateTime.Now.Previous(DayOfWeek.Monday);
var sunday = DateTime.Now.Previous(DayOfWeek.Sunday);
Ugly but it at least gives the right dates back
With start of week set by system:
public static DateTime FirstDateInWeek(this DateTime dt)
{
while (dt.DayOfWeek != System.Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat.FirstDayOfWeek)
dt = dt.AddDays(-1);
return dt;
}
Without:
public static DateTime FirstDateInWeek(this DateTime dt, DayOfWeek weekStartDay)
{
while (dt.DayOfWeek != weekStartDay)
dt = dt.AddDays(-1);
return dt;
}
Let's combine the culture-safe answer and the extension method answer:
public static class DateTimeExtensions
{
public static DateTime StartOfWeek(this DateTime dt, DayOfWeek startOfWeek)
{
System.Globalization.CultureInfo ci = System.Threading.Thread.CurrentThread.CurrentCulture;
DayOfWeek fdow = ci.DateTimeFormat.FirstDayOfWeek;
return DateTime.Today.AddDays(-(DateTime.Today.DayOfWeek- fdow));
}
}
This would give you the preceding Sunday (I think):
DateTime t = DateTime.Now;
t -= new TimeSpan ((int) t.DayOfWeek, 0, 0, 0);
For Monday
DateTime startAtMonday = DateTime.Now.AddDays(DayOfWeek.Monday - DateTime.Now.DayOfWeek);
For Sunday
DateTime startAtSunday = DateTime.Now.AddDays(DayOfWeek.Sunday- DateTime.Now.DayOfWeek);
This may be a bit of a hack, but you can cast the .DayOfWeek property to an int (it's an enum and since its not had its underlying data type changed it defaults to int) and use that to determine the previous start of the week.
It appears the week specified in the DayOfWeek enum starts on Sunday, so if we subtract 1 from this value that'll be equal to how many days the Monday is before the current date. We also need to map the Sunday (0) to equal 7 so given 1 - 7 = -6 the Sunday will map to the previous Monday:-
DateTime now = DateTime.Now;
int dayOfWeek = (int)now.DayOfWeek;
dayOfWeek = dayOfWeek == 0 ? 7 : dayOfWeek;
DateTime startOfWeek = now.AddDays(1 - (int)now.DayOfWeek);
The code for the previous Sunday is simpler as we don't have to make this adjustment:-
DateTime now = DateTime.Now;
int dayOfWeek = (int)now.DayOfWeek;
DateTime startOfWeek = now.AddDays(-(int)now.DayOfWeek);
using System;
using System.Globalization;
namespace MySpace
{
public static class DateTimeExtention
{
// ToDo: Need to provide culturaly neutral versions.
public static DateTime GetStartOfWeek(this DateTime dt)
{
DateTime ndt = dt.Subtract(TimeSpan.FromDays((int)dt.DayOfWeek));
return new DateTime(ndt.Year, ndt.Month, ndt.Day, 0, 0, 0, 0);
}
public static DateTime GetEndOfWeek(this DateTime dt)
{
DateTime ndt = dt.GetStartOfWeek().AddDays(6);
return new DateTime(ndt.Year, ndt.Month, ndt.Day, 23, 59, 59, 999);
}
public static DateTime GetStartOfWeek(this DateTime dt, int year, int week)
{
DateTime dayInWeek = new DateTime(year, 1, 1).AddDays((week - 1) * 7);
return dayInWeek.GetStartOfWeek();
}
public static DateTime GetEndOfWeek(this DateTime dt, int year, int week)
{
DateTime dayInWeek = new DateTime(year, 1, 1).AddDays((week - 1) * 7);
return dayInWeek.GetEndOfWeek();
}
}
}
Putting it all together, with Globalization and allowing for specifying the first day of the week as part of the call we have
public static DateTime StartOfWeek ( this DateTime dt, DayOfWeek? firstDayOfWeek )
{
DayOfWeek fdow;
if ( firstDayOfWeek.HasValue )
{
fdow = firstDayOfWeek.Value;
}
else
{
System.Globalization.CultureInfo ci = System.Threading.Thread.CurrentThread.CurrentCulture;
fdow = ci.DateTimeFormat.FirstDayOfWeek;
}
int diff = dt.DayOfWeek - fdow;
if ( diff < 0 )
{
diff += 7;
}
return dt.AddDays( -1 * diff ).Date;
}
Step 1:
Create a static class
public static class TIMEE
{
public static DateTime StartOfWeek(this DateTime dt, DayOfWeek startOfWeek)
{
int diff = (7 + (dt.DayOfWeek - startOfWeek)) % 7;
return dt.AddDays(-1 * diff).Date;
}
public static DateTime EndOfWeek(this DateTime dt, DayOfWeek startOfWeek)
{
int diff = (7 - (dt.DayOfWeek - startOfWeek)) % 7;
return dt.AddDays(1 * diff).Date;
}
}
Step 2: Use this class to get both start and end day of the week
DateTime dt = TIMEE.StartOfWeek(DateTime.Now ,DayOfWeek.Monday);
DateTime dt1 = TIMEE.EndOfWeek(DateTime.Now, DayOfWeek.Sunday);
var now = System.DateTime.Now;
var result = now.AddDays(-((now.DayOfWeek - System.Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat.FirstDayOfWeek + 7) % 7)).Date;
This would give you midnight on the first Sunday of the week:
DateTime t = DateTime.Now;
t -= new TimeSpan ((int) t.DayOfWeek, t.Hour, t.Minute, t.Second);
This gives you the first Monday at midnight:
DateTime t = DateTime.Now;
t -= new TimeSpan ((int) t.DayOfWeek - 1, t.Hour, t.Minute, t.Second);
Try with this in C#. With this code you can get both the first date and last date of a given week. Here Sunday is the first day and Saturday is the last day, but you can set both days according to your culture.
DateTime firstDate = GetFirstDateOfWeek(DateTime.Parse("05/09/2012").Date, DayOfWeek.Sunday);
DateTime lastDate = GetLastDateOfWeek(DateTime.Parse("05/09/2012").Date, DayOfWeek.Saturday);
public static DateTime GetFirstDateOfWeek(DateTime dayInWeek, DayOfWeek firstDay)
{
DateTime firstDayInWeek = dayInWeek.Date;
while (firstDayInWeek.DayOfWeek != firstDay)
firstDayInWeek = firstDayInWeek.AddDays(-1);
return firstDayInWeek;
}
public static DateTime GetLastDateOfWeek(DateTime dayInWeek, DayOfWeek firstDay)
{
DateTime lastDayInWeek = dayInWeek.Date;
while (lastDayInWeek.DayOfWeek != firstDay)
lastDayInWeek = lastDayInWeek.AddDays(1);
return lastDayInWeek;
}
I tried several, but I did not solve the issue with a week starting on a Monday, resulting in giving me the coming Monday on a Sunday. So I modified it a bit and got it working with this code:
int delta = DayOfWeek.Monday - DateTime.Now.DayOfWeek;
DateTime monday = DateTime.Now.AddDays(delta == 1 ? -6 : delta);
return monday;
The same for end of the week (in style of Compile This's answer):
public static DateTime EndOfWeek(this DateTime dt)
{
int diff = 7 - (int)dt.DayOfWeek;
diff = diff == 7 ? 0 : diff;
DateTime eow = dt.AddDays(diff).Date;
return new DateTime(eow.Year, eow.Month, eow.Day, 23, 59, 59, 999) { };
}
Thanks for the examples. I needed to always use the "CurrentCulture" first day of the week and for an array I needed to know the exact Daynumber.. so here are my first extensions:
public static class DateTimeExtensions
{
//http://stackoverflow.com/questions/38039/how-can-i-get-the-datetime-for-the-start-of-the-week
//http://stackoverflow.com/questions/1788508/calculate-date-with-monday-as-dayofweek1
public static DateTime StartOfWeek(this DateTime dt)
{
//difference in days
int diff = (int)dt.DayOfWeek - (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek; //sunday=always0, monday=always1, etc.
//As a result we need to have day 0,1,2,3,4,5,6
if (diff < 0)
{
diff += 7;
}
return dt.AddDays(-1 * diff).Date;
}
public static int DayNoOfWeek(this DateTime dt)
{
//difference in days
int diff = (int)dt.DayOfWeek - (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek; //sunday=always0, monday=always1, etc.
//As a result we need to have day 0,1,2,3,4,5,6
if (diff < 0)
{
diff += 7;
}
return diff + 1; //Make it 1..7
}
}
Here is a correct solution. The following code works regardless if the first day of the week is a Monday or a Sunday or something else.
public static class DateTimeExtension
{
public static DateTime GetFirstDayOfThisWeek(this DateTime d)
{
CultureInfo ci = System.Threading.Thread.CurrentThread.CurrentCulture;
var first = (int)ci.DateTimeFormat.FirstDayOfWeek;
var current = (int)d.DayOfWeek;
var result = first <= current ?
d.AddDays(-1 * (current - first)) :
d.AddDays(first - current - 7);
return result;
}
}
class Program
{
static void Main()
{
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-US");
Console.WriteLine("Current culture set to en-US");
RunTests();
Console.WriteLine();
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("da-DK");
Console.WriteLine("Current culture set to da-DK");
RunTests();
Console.ReadLine();
}
static void RunTests()
{
Console.WriteLine("Today {1}: {0}", DateTime.Today.Date.GetFirstDayOfThisWeek(), DateTime.Today.Date.ToString("yyyy-MM-dd"));
Console.WriteLine("Saturday 2013-03-02: {0}", new DateTime(2013, 3, 2).GetFirstDayOfThisWeek());
Console.WriteLine("Sunday 2013-03-03: {0}", new DateTime(2013, 3, 3).GetFirstDayOfThisWeek());
Console.WriteLine("Monday 2013-03-04: {0}", new DateTime(2013, 3, 4).GetFirstDayOfThisWeek());
}
}
Modulo in C# works bad for -1 mod 7 (it should be 6, but C# returns -1)
so... a "one-liner" solution to this will look like this :)
private static DateTime GetFirstDayOfWeek(DateTime date)
{
return date.AddDays(
-(((int)date.DayOfWeek - 1) -
(int)Math.Floor((double)((int)date.DayOfWeek - 1) / 7) * 7));
}
I did it for Monday, but with similar logic for Sunday.
public static DateTime GetStartOfWeekDate()
{
// Get today's date
DateTime today = DateTime.Today;
// Get the value for today. DayOfWeek is an enum with 0 being Sunday, 1 Monday, etc
var todayDayOfWeek = (int)today.DayOfWeek;
var dateStartOfWeek = today;
// If today is not Monday, then get the date for Monday
if (todayDayOfWeek != 1)
{
// How many days to get back to Monday from today
var daysToStartOfWeek = (todayDayOfWeek - 1);
// Subtract from today's date the number of days to get to Monday
dateStartOfWeek = today.AddDays(-daysToStartOfWeek);
}
return dateStartOfWeek;
}
The following method should return the DateTime that you want. Pass in true for Sunday being the first day of the week, false for Monday:
private DateTime getStartOfWeek(bool useSunday)
{
DateTime now = DateTime.Now;
int dayOfWeek = (int)now.DayOfWeek;
if(!useSunday)
dayOfWeek--;
if(dayOfWeek < 0)
{// day of week is Sunday and we want to use Monday as the start of the week
// Sunday is now the seventh day of the week
dayOfWeek = 6;
}
return now.AddDays(-1 * (double)dayOfWeek);
}
You could use the excellent Umbrella library:
using nVentive.Umbrella.Extensions.Calendar;
DateTime beginning = DateTime.Now.BeginningOfWeek();
However, they do seem to have stored Monday as the first day of the week (see the property nVentive.Umbrella.Extensions.Calendar.DefaultDateTimeCalendarExtensions.WeekBeginsOn), so that previous localized solution is a bit better. Unfortunate.
Edit: looking closer at the question, it looks like Umbrella might actually work for that too:
// Or DateTime.Now.PreviousDay(DayOfWeek.Monday)
DateTime monday = DateTime.Now.PreviousMonday();
DateTime sunday = DateTime.Now.PreviousSunday();
Although it's worth noting that if you ask for the previous Monday on a Monday, it'll give you seven days back. But this is also true if you use BeginningOfWeek, which seems like a bug :(.
Following on from Compile This' answer, use the following method to obtain the date for any day of the week:
public static DateTime GetDayOfWeek(DateTime dateTime, DayOfWeek dayOfWeek)
{
var monday = dateTime.Date.AddDays((7 + (dateTime.DayOfWeek - DayOfWeek.Monday) % 7) * -1);
var diff = dayOfWeek - DayOfWeek.Monday;
if (diff == -1)
{
diff = 6;
}
return monday.AddDays(diff);
}
This will return both the beginning of the week and the end of the week dates:
private string[] GetWeekRange(DateTime dateToCheck)
{
string[] result = new string[2];
TimeSpan duration = new TimeSpan(0, 0, 0, 0); //One day
DateTime dateRangeBegin = dateToCheck;
DateTime dateRangeEnd = DateTime.Today.Add(duration);
dateRangeBegin = dateToCheck.AddDays(-(int)dateToCheck.DayOfWeek);
dateRangeEnd = dateToCheck.AddDays(6 - (int)dateToCheck.DayOfWeek);
result[0] = dateRangeBegin.Date.ToString();
result[1] = dateRangeEnd.Date.ToString();
return result;
}
I have posted the complete code for calculating the begin/end of week, month, quarter and year on my blog
ZamirsBlog
Calculating this way lets you choose which day of the week indicates the start of a new week (in the example I chose Monday).
Note that doing this calculation for a day that is a Monday will give the current Monday and not the previous one.
//Replace with whatever input date you want
DateTime inputDate = DateTime.Now;
//For this example, weeks start on Monday
int startOfWeek = (int)DayOfWeek.Monday;
//Calculate the number of days it has been since the start of the week
int daysSinceStartOfWeek = ((int)inputDate.DayOfWeek + 7 - startOfWeek) % 7;
DateTime previousStartOfWeek = inputDate.AddDays(-daysSinceStartOfWeek);
I work with a lot of schools, so correctly using Monday as the first day of the week is important here.
A lot of the most terse answers here don't work on Sunday -- we often end up returning the date of tomorrow on Sunday, which is not good for running a report on last week's activities.
Here's my solution, which returns last Monday on Sunday, and today on Monday.
// Adding 7 so remainder is always positive; Otherwise % returns -1 on Sunday.
var daysToSubtract = (7 + (int)today.DayOfWeek - (int)DayOfWeek.Monday) % 7;
var monday = today
.AddDays(-daysToSubtract)
.Date;
Remember to use a method parameter for "today" so it's unit testable!!
Here is a combination of a few of the answers. It uses an extension method that allows the culture to be passed in. If one is not passed in, the current culture is used. This will give it maximum flexibility and reuse.
/// <summary>
/// Gets the date of the first day of the week for the date.
/// </summary>
/// <param name="date">The date to be used</param>
/// <param name="cultureInfo">If none is provided, the current culture is used</param>
/// <returns>The date of the beggining of the week based on the culture specifed</returns>
public static DateTime StartOfWeek(this DateTime date, CultureInfo cultureInfo=null) =>
date.AddDays(-1 * (7 + (date.DayOfWeek - (cultureInfo ?? CultureInfo.CurrentCulture).DateTimeFormat.FirstDayOfWeek)) % 7).Date;
Example Usage:
public static void TestFirstDayOfWeekExtension() {
DateTime date = DateTime.Now;
foreach(System.Globalization.CultureInfo culture in CultureInfo.GetCultures(CultureTypes.UserCustomCulture | CultureTypes.SpecificCultures)) {
Console.WriteLine($"{culture.EnglishName}: {date.ToShortDateString()} First Day of week: {date.StartOfWeek(culture).ToShortDateString()}");
}
}
If you want Saturday or Sunday or any day of week, but not exceeding the current week (Sat-Sun), I got you covered with this piece of code.
public static DateTime GetDateInCurrentWeek(this DateTime date, DayOfWeek day)
{
var temp = date;
var limit = (int)date.DayOfWeek;
var returnDate = DateTime.MinValue;
if (date.DayOfWeek == day)
return date;
for (int i = limit; i < 6; i++)
{
temp = temp.AddDays(1);
if (day == temp.DayOfWeek)
{
returnDate = temp;
break;
}
}
if (returnDate == DateTime.MinValue)
{
for (int i = limit; i > -1; i++)
{
date = date.AddDays(-1);
if (day == date.DayOfWeek)
{
returnDate = date;
break;
}
}
}
return returnDate;
}
We like one-liners: Get the difference between the current culture's first day of week and the current day, and then subtract the number of days from the current day:
var weekStartDate = DateTime.Now.AddDays(-((int)now.DayOfWeek - (int)DateTimeFormatInfo.CurrentInfo.FirstDayOfWeek));

Categories