Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Find Specified US Federal Holiday

2 Feb 2017 1  
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.

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.

/// <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.

/// <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:

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:

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here