reptile7's JavaScript blog
Saturday, March 29, 2014
 
Holiday, It Would Be So Nice
Blog Entry #314

Welcome back to our discussion of the Java Goodies "Post Next Holiday" script. Having worked through the script's original, 1999-based code in the previous post, we will today update and generalize the script so that it's good for 2014 and beyond.

It is not possible to write a single Post Next Holiday script that applies to everyone. We will work with the 11 U.S. federal holidays as a common denominator* in this post as these holidays affect all Americans directly or indirectly. Moreover, the code below should be readily adaptable to holidays in other countries.

*I recognize that a lot of people don't get all of these days off and some people don't get any of them off, and you are of course free to subtract particular holidays if you want to do that.

With respect to determining their observation dates:
• Six U.S. federal holidays are pegged to a specific weekday: Birthday of Martin Luther King, Jr., Washington's Birthday, Memorial Day, Labor Day, Columbus Day, and Thanksgiving Day.
• Five U.S. federal holidays are pegged to a specific date and can therefore fall on a weekend: New Year's Day, Inauguration Day, Independence Day, Veterans Day, and Christmas Day.
We will separately address these holiday groups in the following sections.

Seize the day

The Birthday of Martin Luther King, Jr., holiday is observed on the third Monday of January; depending on what day of the week January begins on, this holiday could be January 15 or 16 or 17 or 18 or 19 or 20 or 21. We can easily nail down the actual date for any given year with:

var today = new Date( );
var thisyear = today.getFullYear( );

for (var i = 15; i < 22; i++) { var thirdweekday = new Date(thisyear, 0, i); if (thirdweekday.getDay( ) == 1) { var mlkday = thirdweekday; break; } }

I prefer the

new Date(year, month [, day, hour, minute, second, millisecond]);

constructor syntax to the

new Date(dateString);

constructor syntax, so that's what I'm gonna use. The getDay( ) method of the Date object returns 1 for a Monday. Mozilla's current break statement page is here.

Analogous snippets can be written for
Washington's Birthday, which is observed on the third Monday of February,
Memorial Day, which is observed on the last Monday of May,
Labor Day, which is observed on the first Monday of September,
Columbus Day, which is observed on the second Monday of October, and
Thanksgiving Day, which is observed on the fourth Thursday of November.

We previously used this approach to specify standard time ↔ daylight saving time transitions in the DST to the millisecond section of Blog Entry #103.

Save the date, part 2

The Christmas Day holiday is normally held on December 25; if December 25 is a Saturday or a Sunday, then this holiday is generally shifted to the preceding Friday or the following Monday, respectively. We can easily nail down the actual date for any given year with:

var christmas = new Date(thisyear, 11, 25);
if (christmas.getDay( ) == 6) christmas.setDate(24);
if (christmas.getDay( ) == 0) christmas.setDate(26);


The getDay( ) method returns 6 for a Saturday and 0 for a Sunday. The setDate( ) method of the Date object is documented here.

Analogous snippets can be written for
New Year's Day, which is normally held on January 1,
Independence Day, which is normally held on July 4, and
Veterans Day, which is normally held on November 11.
Given that New Year's Day follows Christmas by exactly one week, however, I prefer to get the New Year's Day holiday date with:

var nextnewyear = new Date(thisyear, 11, christmas.getDate( ) + 7);

The Inauguration Day holiday, which is normally held on January 20, is different from the other holidays in this group because
(a) most U.S. Government employees don't get it off,
(b) it only occurs once every four years,
(c) it's observed on Saturday if it falls on a Saturday (it goes to Monday if it falls on a Sunday), and
(d) it's not observed at all (on a different date) if it coincides with the Birthday of Martin Luther King, Jr., holiday.
That said, its holiday date can be determined with:

if (thisyear % 4 == 1) { // If it's an inauguration year var inauguration = new Date(thisyear, 0, 20); if (inauguration.getDay( ) == 0) inauguration.setDate(21); }

As shown above, inauguration years (2009, 2013, 2017, ...) can be conveniently flagged via a modulo operation.

Locating today amongst the holidays

If I had happened to be at work on July 5, 1999 (indday), then the original Post Next Holiday script would have told me that the Next holiday is Independence Day (July 5) vis-à-vis Labor Day, and I don't like that: I very much prefer to use a

(preceding or current holiday date <= today) && (today < next holiday date)

conditional for each next-holiday-setting if statement. Moreover, if we use the getDate( ) method to specify the date numbers in the holiday strings, then we won't need to update those strings every year.

if ((indday <= today) && (today < labor)) holiday = "Labor Day (September " + labor.getDate( ) + ")";

Upon generalizing the script, the if (today <= newyear) { holiday = "New Year's Day (Jan. 1)"; } statement for New Year's Day should be replaced with:

if ((christmas <= today) && (today < nextnewyear)) holiday = "New Year's Day (January " + nextnewyear.getDate( ) + ")";

However, this doesn't mean that we should throw out the newyear Date itself: we'll still need it for the newyear-to-mlkday period.

var newyear = new Date(thisyear, 0, 1);
if ((newyear <= today) && (today < mlkday)) holiday = "Martin Luther King's Birthday (January " + mlkday.getDate( ) + ")";


Our last task is to define a holiday setting for Inauguration Day; bearing in mind that the Inauguration Day holiday can coincide with or follow but not precede the Birthday of Martin Luther King, Jr., holiday, here's how we can do it:

if (inauguration && inauguration != mlkday) { var lastjanholiday = inauguration; if ((mlkday <= today) && (today < inauguration)) holiday = "Inauguration Day (January " + inauguration.getDate( ) + ")"; } else lastjanholiday = mlkday; if ((lastjanholiday <= today) && (today < washington)) holiday = "Washington's Birthday (February " + washington.getDate( ) + ")";

The last holiday in January (lastjanholiday) is usually the Birthday of Martin Luther King, Jr., holiday, but if it's an inauguration year AND if the Inauguration Day and Birthday of Martin Luther King, Jr., holidays do not coincide, then the lastjanholiday is Inauguration Day and a new holiday string is in order.

Demo

Place on your employee intranet to remind users of upcoming holidays automatically!


Comments: Post a Comment

<< Home

Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)