Bookmark and Share

Calendar

See the demo page for the finished version of the code.

In this article, we'll look at some details of the JavaScript object model and see how to extend a predefined JavaScript object.

The Date Object

The JavaScript language comes with a number of predefined objects representing basic data types like Array, Boolean, Number and String. Also among these is the Date object, for storing and manipulating date and time values.

It has several methods you can use it to compare dates, extract individual values like the hour of the day or the month of the year, and even do basic addition and subtraction of date and time values. This is the feature we're interested in.

Date Roll

As an example, we'll create a Date object and initialize it to December 30, 2001. We'll then use the getDate() and setDate() methods to repeatedly bump the date by one day.

Here's the code:

// Set date to December 30, 2001.

myDdate = new Date("12/30/2001")

// Bump the date.

for (i = 0; i < 5; i++) {
  document.writeln(myDate + "<br />");
  myDate.setDate(myDate.getDate() + 1);
}

and below is the result:

Note that when the day of the month increases to 32, it changes to the first of the month, the month changes from December to January and the year goes from 2001 to 2002. The setDate() method understands that there are only 31 days in December, so it moves to the first day of the following month. Likewise, it advances the year as well.

Subseqent calls to getDate(), getMonth and getFullYear() will reflect these changes, returning 1, 0 (for January) and 2002 respectively.

This "roll over" effect can be very useful. For example, to find the date 90 days from today you can simply code:

var expirationDate = new Date();
expirationDate.setDate(expirationDate.getDate() + 90);
alert(expirationDate);

You need not worry about how many days are in each month or whether it's a leap year or not. The setDate() method already has that logic built in.

Negative values work as well. Using setDate(-1) will move the date back one day, rolling back to the last day of the previous month (and year), as necessary.

Month Roll

The setMonth() method works in a similar fashion. Again, here's an example:

// Set date to December 30, 2001.

myDate = new Date("12/30/2001")

// Bump the month.

for (i = 0; i < 5; i++) {
  document.writeln(myDate + "<br />");
  myDate.setMonth(myDate.getMonth() + 1);
}

and the result:

But note the subtle difference here. We start with the 30th of December, then go to the 30th of January. But in February there are only 28 days. The setMonth() method resolves this by making the date March 2nd (two days after the 28th). From then on, as the month is increased by one, the day of the month remains as the second.

That sounds reasonable, but consider this code:

// Set date to December 30, 2001.

myDate = new Date("12/30/2001")
document.writeln(myDate + "<br />");

// Move ahead five months.

myDate.setMonth(myDate.getMonth() + 5);
document.writeln(myDate + "<br />");
}

the result is:

Adding five months, one month at a time, yields March 2nd. But adding five months all at once yields March 30th. Why doesn't it add up? Simple, months do not all have the same number of days. One, February, isn't even consistent. It has 28 days except in leap years when it has 29.

In the first example, adding one month at a time, the roll over occurs when we reach February, which has only 28 days. That results in March 2nd. Since every month has at least two days, it will not roll over again no matter what month we subsequently set the date to. But in the second example, jumping directly from December to May, no roll over is necessary. The original date of the 30th is valid for the new month, May.

That's just how the designers of the Date object chose to implement these methods in JavaScript. But suppose you wanted a different behavior. One where adding one month to a date five times gives the same result as adding five months at once. You can accomplish this by adding your own properties and methods to the existing Date object.

Defining the Custom Behavior

Let's be specific on what we want. If we are just adding or subtracting days from a date, we want it to roll over the month and year like it normally does. But when adding or subtracting months (or years), we do not want the date to roll over.

For example, start with December 30, 2001. If we add two months we want to get February of 2002. There is no February 30th, but instead of rolling over into March, we want to set the date to the last day of February, the 28th.

Also, we want it to "remember" the fact that we started with the 30th of the month, so that if we then added another month we'd get March 30th instead of March 28th.

The demo illustrates this behavior pretty well. It let's you select a date from a calendar display. You can click on a date to highlight it and use the buttons to advance the calendar forward or backward one month or year at a time.

If you highlight the 31st of a month with 31 days, you can then move up and down a month and see how the idea works. It will use the 31st of the month when possible or simply the last day of the month if not.

Adding and subtracting years works the same way. If you highlight February 29th of a leap year, moving up or down a year will result in February 28th. But if you keep going until you reach another leap year, it will jump back to the 29th.