Given a DateTime and a DayOfWeek should return the date of the last DayOfWeek of that month.
E.g. 1st-March-2009 and Sunday would return 29th-March-2009
Can't find a handy one-liner but this one works:
static DateTime LastDayOfWeekInMonth(DateTime day, DayOfWeek dow)
{
DateTime lastDay = new DateTime(day.Year, day.Month, 1).AddMonths(1).AddDays(-1);
DayOfWeek lastDow = lastDay.DayOfWeek;
int diff = dow - lastDow;
if (diff > 0) diff -= 7;
System.Diagnostics.Debug.Assert(diff <= 0);
return lastDay.AddDays(diff);
}
http://datetimeextensions.codeplex.com/ has a Last(dayOfWeek) extension method that will do this for you
Test included :-)
Get the first day in the next month, then find the first mathing weekday in that month. Go back seven days and you are at that weekday in the last week of the corrent month:
DateTime date = new DateTime(2009, 3, 12);
DayOfWeek day = DayOfWeek.Sunday;
DateTime nextMonth = new DateTime(date.Year, date.Month, 1).AddMonths(1);
while (nextMonth.DayOfWeek != day) {
nextMonth = nextMonth.AddDays(1);
}
DateTime lastInMonth = nextMonth.AddDays(-7);
(You could replace the loop with some arithmetic that calculates the number of days to add based on the numeric values of the DayOfWeek values, but this is more straight forward.)
Edit:
You could of course also get the last day in the current month, then loop backwards until you find the correct weekday.
static DateTime LastDateOfWeekForMonth(DayOfWeek weekday, int month, int year)
{
DateTime d = new DateTime(year, month, 1).AddMonths(1);
while (!(d.DayOfWeek == weekday && d.Month == month))
{
d = d.AddDays(-1);
}
return d;
}
public DateTime GetLastDayOfMonth(int year, int month, DayOfWeek dayOfWeek)
{
var daysInMonth = DateTime.DaysInMonth(year, month);
var lastDay = new DateTime(year, month, daysInMonth);
while (lastDay.DayOfWeek != dayOfWeek)
{
lastDay = lastDay.AddDays(-1);
}
return lastDay;
}
Try this: start at the last day and count back until you hit the day you're looking for
static DateTime LastOccurenceOfDay(DateTime dt, DayOfWeek dow)
{
DateTime looperDate = new DateTime(dt.Year, dt.Month, 1)
.AddMonths(1).AddDays(-1);
while (looperDate.DayOfWeek!=dow)
looperDate =looperDate.AddDays(-1);
return looperDate;
Edit: Recovered the old version Dangph is refering to in the comments.
static DateTime LastOccurenceOfDay(DateTime dt, DayOfWeek dow)
{
//set start of loop
DateTime looperDate = new DateTime(dt.Year, dt.Month, 1);
//initialze return value
DateTime returnDate = dt;
//loop until the month is over
while (looperDate.Month == dt.Month)
{
//if the current DayOfWeek is the date you're looking for
if (looperDate.DayOfWeek == dow)
//remember it.
returnDate = looperDate;
//increase day
looperDate=looperDate.AddDays(1);
}
return returnDate;
}
Here is both LastDayOfMonth() and LastDayOfMonth(DayOfWeek) plus unit tests, which were inspired by the "Last Friday" implementation in How to find the 3rd Friday in a month with C#? by @Paul Fryer:
/**
/// <summary>An extension method that returns the last day of the month.</summary>
/// <param name="d">A date within the month to calculate.</param>
/// <returns>The last day of the current month.</returns>
**/
public static DateTime LastDayOfMonth(this DateTime d)
{
DateTime nextMonth = new DateTime(d.Year, d.Month, 1).AddMonths(1);
return nextMonth.AddDays(-1);
}
/**
/// <summary>An extension method that returns the last <see cref="DayOfWeek"> of the month.</summary>
/// <param name="d">A date within the month to calculate.</param>
/// <returns>The last day of the current month.</returns>
**/
public static DateTime LastDayOfMonth(this DateTime d, DayOfWeek dayOfWeek)
{
DateTime lastDayOfMonth = d.LastDayOfMonth();
int vector = (((int)lastDayOfMonth.DayOfWeek - (int)dayOfWeek + DaysInWeek) % DaysInWeek);
return lastDayOfMonth.AddDays(-vector);
}
#region LastDayOfMonth Tests
[TestCase("1/1/2011", "1/31/2011")]
[TestCase("2/1/2009", "2/28/2009")] //Leap Year
[TestCase("2/1/2008", "2/29/2008")] //Leap Year
[TestCase("3/10/2011", "3/31/2011")]
[TestCase("4/20/2011", "4/30/2011")]
[TestCase("5/15/2011", "5/31/2011")]
[TestCase("6/30/2011", "6/30/2011")]
[TestCase("12/1/2011", "12/31/2011")]
public void LastDayOfMonthReturnsCorrectDay(string startingDate, string expectedDate)
{
//Arrange
DateTime testDate = DateTime.Parse(startingDate);
DateTime expected = DateTime.Parse(expectedDate);
//Act
DateTime actual = testDate.LastDayOfMonth();
//Assert
Assert.That(actual, Is.EqualTo(expected));
}
[TestCase("1/1/2011", DayOfWeek.Monday, "1/31/2011")]
[TestCase("2/1/2009", DayOfWeek.Saturday, "2/28/2009")]
[TestCase("2/1/2009", DayOfWeek.Sunday, "2/22/2009")]
[TestCase("2/1/2008", DayOfWeek.Friday, "2/29/2008")] //Leap Year
[TestCase("2/1/2008", DayOfWeek.Thursday, "2/28/2008")] //Leap Year
[TestCase("3/10/2011", DayOfWeek.Wednesday, "3/30/2011")]
[TestCase("4/20/2011", DayOfWeek.Friday, "4/29/2011")]
[TestCase("4/20/2011", DayOfWeek.Saturday, "4/30/2011")]
[TestCase("5/15/2011", DayOfWeek.Tuesday, "5/31/2011")]
[TestCase("5/15/2011", DayOfWeek.Saturday, "5/28/2011")]
[TestCase("9/30/2011", DayOfWeek.Sunday, "9/25/2011")]
[TestCase("12/1/2011", DayOfWeek.Monday, "12/26/2011")]
public void LastDayOfMonthReturnsCorrectDayOfWeek(string startingDate, DayOfWeek dayOfWeek, string expectedDate)
{
//Arrange
DateTime testDate = DateTime.Parse(startingDate);
DateTime expected = DateTime.Parse(expectedDate);
//Act
DateTime actual = testDate.LastDayOfMonth(dayOfWeek);
//Assert
Assert.That(actual, Is.EqualTo(expected));
}
#endregion
static DateTime GetDayOfWeekInMonthFromWeekIndex(this DateTime date, DayOfWeek dow, int weekIndex)
{
if (weekIndex > 4)
return LastDayOfWeekInMonth(date, dow);
DateTime newDate = new DateTime(date.Year, date.Month, 1);
DayOfWeek newDow = newDate.DayOfWeek;
int diff = dow - newDow;
diff += ((weekIndex - 1) * 7);
return newDate.AddDays(diff);
}