Holidays and Not Today

One benefit of my new approach to producing calendars by adding a method to the date object is that it makes it far more flexible as to where it can be used. Where my original calendar script could only be used in its entirety and was useless if a calendar was needed for a different purpose, this new script is far more adaptable for any purpose involving a calendar.

When someone complained on a forum that they couldn't adapt the calendar script they'd found to display public holidays and to only highlight the current date when displaying the current month, I immediately thought of adding that functionality to my toCalendar method and offering that as a replacement for the script they were using. Someone else was able to quickly provide the extra code so they could display the calendars as they wanted (which I believe was to show all 12 months of the year in two columns down the one page).

After quickly adding the extra code to provide those two extra options it then occurred to me that by adding a couple of extra lines of code I could make those extra options even more flexible. So let's look at these changes and consider what effect they have on how much more usable they make the toCalendar method.

The new code got added in just two spots in the existing method - at the start of the script and at the end - so I'll save some space here by just repeating enough of the prior version of the script so you can see where the extra pieces fit:

Date.prototype.toCalendar = function(h,c) {
"use strict";
var tabl, row, i, j, r, dow, mth, n, w, e, x, hol;
hol = function(d,w,h) {
     var dt, p;
     dt = d.getFullYear()+'-';
     p = d.getMonth()+1;
     dt += (p>9)?p:'0'+p;
     dt += '-';
     dt += (w>9)?w:'0'+w;
     for(var i = h.length-1; i >= 0; i--) {
       if (h[i] === dt) return true;}
h = h || [];
if (typeof h==='function') h = h(this.getFullYear());
if (typeof h==='boolean') {c = h; h = [];}
e = new Date(this.getFullYear(), this.getMonth()+1,0);
x = e.getDate();
// main part of script (unchanged)
      if (w < n+1 || w-n > x)
         row.cells[i].innerHTML = ' ';
      else {
         row.cells[i].innerHTML = w-n;
         if (this.getDate()===w-n && !c)
         if (h.length > 0 && hol(this,w-n,h))
           row.cells[i].className+=' holiday';
   return tabl;

The biggest addition to the script is the hol() function. This receives three parameters - the date object that the toCalendar method is attached to (d), the day of the month that we are up to in generating the calendar (w) and an array of holiday dates as text strings in the format ccyy-mm-dd (h). The function gets the year and month from the date object and appends the current day of the month to give a text string that can be compared to all the dates in the holiday array. The function returns true if it finds that date in the array and false if it doesn't.

Let's skip now to the amended code at the bottom of the script. The if statement testing if the current day of the month being generated for the calendar is the same day as is stored in the date object now has a second condition to test. The method now accepts a true/false value as a parameter to identify whether the current day should be highlighted and so we only highlight the day if that parameter evaluates to false (by using true to suppress the highlighting we maintain the existing functionality if the parameter is omitted since then it is undefined and undefined evaluates to false).

An additional if statement added immediately afterwards adds a further test. If the holiday array is not empty then it calls the hols() function to determine if the day should be highlighted as a holiday. Since both lots of highlighting are done by adding classes to the table cell elements and the code allows both to be added to the same element when today is a holiday we can determine which takes precedence by the order of the classes in the stylesheet (or even allow both to apply if they highlight in different ways).

The three statements immediately following the hols() function are also new additions although only the first of these was present in the original solution I offered on the forum. That first statement tests if a value has been supplied in the first parameter. If no parameters have been supplied then it sets the parameter to be an empty array. This preserves the original functionality of the method if no parameters have been supplied and allows both the parameters to be optional.

So if we call toCalendar() we get the original functionality since the holiday array is empty and the parameter to suppress highlighting the current date is false.

If we call toCalendar(holidays) (where 'holidays' is an array of dates) then if any of those dates are in the current month then they will be highlighted as holidays. The current day of the month will be highlighted as well but in a different way.

If we call toCalendar(holidays,true) then we get the holiday dates highlighted in the calendar but the current day of the month is not highlighted (you'd do this when the date is not in the current month).

The two lines after that which I have added more recently make the method polymorphic without having to define alternative methods the way you would in an object oriented language. The test for the first parameter being boolean allows us to omit the holiday array and just specify toCalendar(true) to display a calendar with no days highlighted at all.

The test for the first parameter being a function makes the method way more flexible than we have achieved to this point. Now instead of passing an array of dates we can instead pass a function. The function needs to accept one parameter specifying a year and to return an array of dates. This will allow us to build a function that will dynamically generate the holiday date array for us and supply dates relevant for whichever year that the calendar to be displayed is in.


This article written by Stephen Chapman, Felgall Pty Ltd.

go to top

FaceBook Follow
Twitter Follow