tags:

views:

724

answers:

7

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

+4  A: 

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.

Guffa
@Guffa, thanks for catching that. I was just trying to be the first to answer so my logic was !good.
divitiae
+9  A: 

http://datetimeextensions.codeplex.com/ has a Last(dayOfWeek) extension method that will do this for you

Test included :-)

TFD
Clever implementation, and an extension would be sweet. I'd rename it to something mores specific though. It could be Date.LastDayOfWeekForMonth Date.LastDayOfWeekForYear or Date.LastDayOfWeekCentury Date.LastDayOfWeekForMillenium
divitiae
Great library, thanks TFD! Unfortunately I can't use it as is because I am targeting .NET 2.0 so I've marked Henks answer, but you've got a lot of upvotes for this anyway.
Ryan
+4  A: 

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);
    }
Henk Holterman
Like it, thanks Henk. Minor point - you could remove the last .AddDays(-1) and change "if (diff > 0)" to "if (diff >= 0)" - thus removing 1 datetime.addDays operation.
Ryan
Ryan, yes, it saves 1 op but it is bordering on what the compiler can optimize. And conceptually "last day of the month" is easier on the mind than "first day of next month". So I normally wouldn't optimize this.
Henk Holterman
+1  A: 

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;
     }
Sorskoot
That's your definition of simple?
Jan Jungnickel
yes, if you have a better idea, where's your answer?
Sorskoot
I liked your first version more. This one is harder to think about. And I highly doubt that it could have any detectable performance gain unless you were doing a gazillion of these calculations. I was going to suggest in the old version that you could just add 7 days at a time to make it simpler.
dangph
+3  A: 
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;
}
divitiae
Why the downvote on this one? I put the "!" in!
divitiae
I am guessing the downvote is because using a loop for something like this is unlikely to be optimal - but at least you're starting from the end of the month and working backwards so max 6 iterations.
Ryan
To optimize, if dayofweek > wednesday, do it @Guffa's way, otherwise, mine :)
divitiae
I'd pick simplicity over speed in this case.
divitiae
Erm - yes its very readable but both yours and Guffas solution use looping whilst Henks doesn't, so its likely to be faster (though will be very minimal and you can never be sure without profiling). I've upvoted you anyways, thanks!
Ryan
A: 
    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;
    }
bsnote
A: 
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);
}
amir gandomkar