Click here to Skip to main content
15,882,017 members
Articles / Programming Languages / C#
Tip/Trick

Find Specified US Federal Holiday

Rate me:
Please Sign up or sign in to vote.
5.00/5 (5 votes)
1 Feb 2017CPOL2 min read 10.7K   7   3
Find the date of a specified federal holiday for a given year.

Introduction

While doing the code for the tip here - US Federal Holidays (C#) - I also created a couple of static methods that would allow me to determine the date of a specific Holiday in a given year.

The SQL version of this tip is here.

Background

The code

The following methods and enumerators should be placed exist in a public static class. Since I'm creating extension methods for the .Net DateTime struct, I call my class ExtendDateTime. The name of the class is unimportant, so you can call yours FruitRollup if you'd like to.

We start off by defining an enumerator that identifies the holidays in which we are interested.

C#
public enum Holiday { NewYears, MLKDay, PresidentsDay, MemorialDay, IndependenceDay, LaborDay, ColumbusDay, VeteransDay, Thanksgiving, Christmas };

Next, we implement some helper methods. The comments tell the story, so it would be pointless to describe the method in narrative form. Suffice it to say that this is where the magic happens.

C#
/// <summary>
/// Adjusts the holiday date to the "observed" date if the actual holiday falls on a 
/// weekend and the ExtendDateTime.AdjustHolidayForWeekend property is set to true. If the 
/// holiday falls on Saturday, the date is adjusted backwards by one day. If the holiday 
/// falls on Sunday, the date is adjusted foreward by one day. If the holiday doesn't fall 
/// on a weeken, the date parameter is returned unmolested.
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
public static DateTime HolidayOnWeekend(this DateTime date)
{
    DateTime newDate = date;
    if (date.DayOfWeek == DayOfWeek.Saturday)
    {
        newDate = newDate.AddDays(-1);
    }
    else if (date.DayOfWeek == DayOfWeek.Sunday)
    {
        newDate = newDate.AddDays(1);
    }
    return newDate;
}

/// <summary>
/// Finds date of the Nth occurance of the specified day of week in the specified 
/// month/year (for example, to find the fourth Thursday of November of a 
/// specified year).
/// </summary>
/// <param name="year">The year to check (0-9999).</param>
/// <param name="month">The month to check (1-12).</param>
/// <param name="dayOfWeek">The day of week (1-5).</param>
/// <param name="ordinal">The ordinal occurance of the specified day of week.</param>
/// <param name="ordinalDate">The date of the speciified ordinal year/month/day.</param>
/// <returns>True if the parameters and final ordinal date were valid.</returns>
/// <remarks>This method acts in a similar fashion to the TryParse method. If the 
/// parameters and resulting ordinal date are valid, the method returns true, and 
/// you can use the returned ordinalDate. If the return value is false, the 
/// ordinalDate value contains a DateTime with 0 ticks.</remarks>
public static bool GetDateByOrdinalDay(int year, 
                                       int month, 
                                       DayOfWeek dayOfWeek, 
                                       int ordinal,
                                       out DateTime ordinalDate)
{
    // assume failure
    bool result = false;
    ordinalDate = new DateTime(0);

    // Make sure the year, the month, and the ordinal parameters are within the 
    // allowable range before we proceed.
    if (ordinal >= 1 && ordinal <= 5 &&
        month   >= 1 && month   <= 12 &&
        year    >= 0 && year    <= 9999)
    {
        month = Math.Min(Math.Max(month, 1), 12);

        // Set workingDate to the first day of the month.
        DateTime workingDate = new DateTime(year, month, 1);

        // Set workingDate to the first occurrence of the DayOfWeek parameter
        workingDate = (int)workingDate.DayOfWeek > (int)dayOfWeek
                            ? workingDate.AddDays(7 - (int)workingDate.DayOfWeek + (int)dayOfWeek)
                            : workingDate.AddDays((int)dayOfWeek - (int)workingDate.DayOfWeek);
 
        // Advance the date by the number of days required to satisfy the ordinal parameter
        workingDate = workingDate.AddDays((ordinal - 1) * 7);

        // In the event the programmer specified an ordinal that did not cause 
        // the month to change, we can return true and set the ordinal date to 
        // something meaningful.
        if (workingDate.Month == month)
        {
            ordinalDate = workingDate;
            result      = true;
        }
    }

    return result;
}

Finally, we have the reason we're all attending this party. This is the method you call to actually find a specific holiday. The only thing this method does is calls one of the two helper methods shown above.

C#
/// <summary>
/// Finds the specified holiday within the specified year
/// </summary>
/// <param name="year">The year</param>
/// <param name="holiday">The holiday</param>
/// <returns>The DateTime representing the OBSERVED holiday</returns>
public static DateTime FindFederalHoliday(int year, Holiday holiday)
{
    DateTime result = new DateTime(0);
    switch (holiday)
    {
        // specific days
        case Holiday.Christmas       : result = HolidayOnWeekend(new DateTime(year, 12, 25)); break;
        case Holiday.IndependenceDay : result = HolidayOnWeekend(new DateTime(year, 7,  4));  break;
        case Holiday.NewYears        : result = HolidayOnWeekend(new DateTime(year, 1,  1));  break;
        case Holiday.VeteransDay     : result = HolidayOnWeekend(new DateTime(year, 11, 11)); break;
        // nth weekdays
        case Holiday.MemorialDay     : 
            if (GetDateByOrdinalDay(year, 6,  DayOfWeek.Monday, 1, out result))
            {
                result = result.AddDays(-7); 
            }
            break;
        case Holiday.Thanksgiving    : GetDateByOrdinalDay(year, 11, DayOfWeek.Thursday, 4, out result); break;
        case Holiday.ColumbusDay     : GetDateByOrdinalDay(year, 10, DayOfWeek.Monday, 2, out result);   break;
        case Holiday.LaborDay        : GetDateByOrdinalDay(year, 9,  DayOfWeek.Monday, 1, out result);   break;
        case Holiday.MLKDay          : GetDateByOrdinalDay(year, 1,  DayOfWeek.Monday, 3, out result);   break;
        case Holiday.PresidentsDay   : GetDateByOrdinalDay(year, 2,  DayOfWeek.Monday, 3, out result);   break;
    }
    return result;
}

Usage

To find the date for Thanksgiving 2017, for example, all you have to do is:

C#
DateTime thanksgiving = ExtendDateTime.FindHoliday(2017, ExtendDateTime.Holiday.Thanksgiving);

If you have a holiday you want to find the date of that isn't covered in the list of US federal holidays, you can call the appropriate helper method directly. For instance, if you want to find the date on which Mother's Day (2nd Sunday in May) occurs in 2017, you would do this:

C#
DateTime mothersDay = ExtendDateTime.GetDateByOrdinalDay(2017, 5, DayOfWeek.Sunday, 2);

FYI, Easter is not not determined using either of the helper methods described in this tip. If you want to determine the date of Easter Sunday, refer to this tip: When is Easter?

Points of Interest

Chuck Norris ignores The Periodic Table Of Elements, because all he recognizes is the element of Surprise.

History

02 Feb 2017 - Original submission.

License

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


Written By
Software Developer (Senior) Paddedwall Software
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

 
QuestionRedundant code? Pin
gggustafson7-Feb-17 4:08
mvagggustafson7-Feb-17 4:08 
AnswerRe: Redundant code? Pin
#realJSOP7-Feb-17 4:26
mve#realJSOP7-Feb-17 4:26 
GeneralRe: Redundant code? Pin
gggustafson7-Feb-17 4:45
mvagggustafson7-Feb-17 4:45 

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

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