Click here to Skip to main content
Click here to Skip to main content

Get the Nth Instance of a Weekday in the Specified Month

, 12 Nov 2012 CPOL
Rate this:
Please Sign up or sign in to vote.
Presenting a DateTime extension class that allows you to determine the actual date of the specified instance of the specified weekday within the specified month/year.

Introduction 

While writing code for the Home Theater PC software I'm developing, I realized a need to be able to determine the date of the Nth day of a given month for the purpose of determining when some holidays occur. For example, Thanksgiving (in the US) is celebrated on the fourth Thursday of November. This requirement led me to develop the following extension methods for the DateTime class.

The Code

First, I needed to establish the necessary list of parameters. To make it as generic as possible at its most basic level, I needed to know the year, month, and day of week we're looking for, as well as the instance (ordinal) of that day of the week within the specified month.

public static DateTime GetDateByOrdinalDay(this DateTime dt, 
                                                          int year, 
                                                          int month, 
                                                          DayOfWeek day, 
                                                          int ordinal)
{

Next, I needed to perform some sanity checks to make sure the programmer couldn't do something completely stupid (not that we programmers do that a lot). I also wanted the code to adjust appropriately for ordinals that were outside the allowable range. If an ordinal less than 1 is specified, the code self-corrects and assigns 1 to the variable. If the value is greater than 5, it makes the value 5. There are no months that have more than five of any given weekday, so I figure this is a reasonable move. If you want to interrupt the code, you could always modify it to throw an exception instead of correcting itself.

    ordinal = Math.Min(Math.Max(ordinal, 1), 5);
    month = Math.Min(Math.Max(month, 1), 12);

After that, I create a DateTime object that I am free to thrash on (using the 1st day of the year/month), as well as determining the maximum number of days we can add to build the desired date. Since we're looking for the Nth instance of a given weekday in a given month, it doesn't make sense to return a date that falls outside that month. This value will help us later in the method.

    DateTime workingDate = new DateTime(year, month, 1);
    DateTime lastDay = new DateTime(year += (month + 1 > 12) ? 1 : 0, 
                                    month += (month + 1 > 12)? - 11 : 1, 
                                    1).AddDays(-1);
    maxDays = (lastDay - workingDate).Days + 1;

Next, we need to determine how many days to add to the current working date (remember, it represents the first day of the specified month). To start, we need to calculate the gap between the first occurrence of the specified DayOfWeek, and the first day of the month.

    int gap = 0;

    // if the the day of week of the first day is NOT the specified day of week
    if (workingDate.DayOfWeek != dayOfWeek)
    {
        // determine the number of days between the first of the month and 
        // the first instance of the specified day of week
        gap = (int)workingDate.DayOfWeek - (int)dayOfWeek;
        gap = (gap < 0) ? Math.Abs(gap) : 7 - gap;

        // and set the date to the first instance of the specified day of week
        workingDate = workingDate.AddDays(gap);
    }

At this point, our working date is set to the first instance (ordinal=1) of the specified DayOfWeek, so all we have to do is add enough days to accommodate the specified ordinal value (and we only have to do it if the specified ordinal is greater than 1.

    // if we want something later than the first instance
    if (ordinal > 1)
    {
        // determine how many days we're going to add to the working date to 
        // satisfy the specified ordinal
        int daysToAdd = 7 * (ordinal - 1);

        // now adjust back, just in  case the specified ordinal - this loop 
        // should only iterate once or twice
        while (daysToAdd + gap > maxDays-1)
        {
            daysToAdd -= 7;
        }

        // finally we adjust the date by the number of days to add
        workingDate = workingDate.AddDays(daysToAdd);
    }

Finally, we return the calculated date.

Usage

Usage is simple:

rDateTime thanksgiving = DateTime.GetDateByOrdinalDay(DateTime.Now.Year, 
                             11, 
                             System.DayOfWeek.Thursday, 
                             4);

Final Notes

The class included in the download includes an overloaded version of this method that assumes that the current year is to be used, as well as a Set... method to allow the calling object to set itself to the result.

The original version of the code used recursion, which I replaced with a while... loop. The reason for the sanity checks is that I try to avoid using exceptions if at all possible. Since there's no reason the code shouldn't be able to self-correct, that's the approach I took.

Have a ball.

History

12 Nov 2012 - Refactored the main method in the extension class because I found some issues under certain circumstances. Aug 2012 - Original Article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

John Simmons / outlaw programmer
Software Developer (Senior)
United States United States
I've been paid as a programmer since 1982 with experience in Pascal, and C++ (both self-taught), and began writing Windows programs in 1991 using Visual C++ and MFC. In the 2nd half of 2007, I started writing C# Windows Forms and ASP.Net applications, and have since done WPF, Silverlight, WCF, web services, and Windows services.
 
My weakest point is that my moments of clarity are too brief to hold a meaningful conversation that requires more than 30 seconds to complete. Thankfully, grunts of agreement are all that is required to conduct most discussions without committing to any particular belief system.

Comments and Discussions

 
QuestionAlternative? PinmemberGeorge Swan12-Nov-12 22:23 
AnswerRe: Alternative? PinmvpJohn Simmons / outlaw programmer13-Nov-12 4:21 
GeneralRe: Alternative? PinmemberGeorge Swan13-Nov-12 12:33 
GeneralRe: Alternative? PinmvpJohn Simmons / outlaw programmer14-Nov-12 4:36 
GeneralRe: Alternative? [modified] PinmemberGeorge Swan15-Nov-12 0:14 
GeneralRe: Alternative? PinmemberM.Passing10-Feb-13 13:38 
GeneralMy vote of 5 PinmemberIan Shlasko10-Aug-12 6:17 
GeneralRe: My vote of 5 PinmvpJohn Simmons / outlaw programmer12-Nov-12 7:50 
GeneralMy vote of 5 PinmvpJohn Simmons / outlaw programmer9-Aug-12 7:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141220.1 | Last Updated 12 Nov 2012
Article Copyright 2012 by John Simmons / outlaw programmer
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid