-->
Bookmark and Share

Calendar

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

The New Date Object

Now let's use the object prototype to extend the predefined Date object to make dates behave the way we want for the calendar example.

// Properties

Date.prototype.monthNames = new Array("January", "February", "March",
  "April", "May", "June", "July", "August", "September", "October",
  "November", "December");
Date.prototype.savedDate  = null;

// Methods

Date.prototype.getMonthName = dateGetMonthName;
Date.prototype.getDays      = dateGetDays;
Date.prototype.addDays      = dateAddDays;
Date.prototype.addMonths    = dateAddMonths;
Date.prototype.addYears     = dateAddYears;

The monthNames property is simply an array of string constants. The getMonthName() method uses it to map the date's numeric month value to a text string.

function dateGetMonthName() {

  return this.monthNames[this.getMonth()];
}

The savedDate property will be used to keep track of the original day of the month, as we'll see later on. The getDays() method will be used to determine how many days are in the current month of a given date.

The other methods handle the addition or subtraction of days, months and years to a date object. This is where we can change the roll over behavior discussed earlier.

Adding Days

This is the simplest of the new methods as we're going to let the Date object use its default roll over logic.

function dateAddDays(n) {

  // Add the specified number of days.

  this.setDate(this.getDate() + n);

  // Reset the new day of month.

  this.savedDate = this.getDate();
}

The only difference is that we update the savedDate property to match the new day of the month. This is the only place where that value is updated.

Note that n can be negative, meaning that you can subtract days as well as add them.

Adding Months

Here's where it gets a little more complicated, because we want to change the default roll over behavior.

function dateAddMonths(n) {

  // Save the day of month if not already set.

  if (this.savedDate == null)
    this.savedDate = this.getDate();

  // Set the day of month to the first to avoid rolling.

  this.setDate(1);

  // Add the specified number of months.

  this.setMonth(this.getMonth() + n);

  // Restore the saved day of month, if possible.

  this.setDate(Math.min(this.savedDate, this.getDays()));
}

First, we check savedDate, if it's not already set we assign it the current day of the month.

Next, we set the date to the first of the month. This will prevent any unwanted roll over if we are adding, say one month to October 31. Since November has only 30 days, this would cause the date to roll over into December, something we don't want.

Now we add the specified number of months. This takes us to the first of the new month so we need to restore the day of the month. Whatever we do, we don't want to set it higher than the number of days this new month has because it would then roll over into the next month.

So it compares the saved date to the number of days in the new month, taking the lesser of the two using the built in Math.min() method. Using the same example as before, moving one month from October 31 to November, the minimum of 31 and 30 is 30. So the date becomes November 30, the last day of November.

The savedDate property is still set to 31, so adding another month would give December 31, the same result we would have had adding two months to October 31.

Again, adding a negative number of months works just like subtracting months from the date.

Adding Years

Adding years works just like adding months. We avoid unwanted roll overs by setting the date to the first of the month before changing the year.

function dateAddYears(n) {

  // Save the day of month if not already set.

  if (this.savedDate == null)
    this.savedDate = this.getDate();

  // Set the day of month to the first to avoid rolling.

  this.setDate(1);

  // Add the specified number of years.

  this.setFullYear(this.getFullYear() + n);

  // Restore the saved day of month, if possible.

  this.setDate(Math.min(this.savedDate, this.getDays()));
}

And again, we add or subtract the specified number of years and set the day of the month using the lesser of savedDate and getDays().

Finding the Number of Days in a Month

The last custom method added to the Date object was getDays(), used by the methods above. This method simply returns the number of days in the date's current month.

function dateGetDays() {

  var tmpDate, d, m;

  tmpDate = new Date(Date.parse(this));
  m = tmpDate.getMonth();
  d = 28;
  do {
    d++;
    tmpDate.setDate(d);
  } while (tmpDate.getMonth() == m);

  return d - 1;
}

To find this value, we create a temporary copy of the date. This copy of the date is then set to the 28th of the month (the least number of days any month can have). Looping, we bump it to the 29th, then the 30th, etc. until the date rolls over. That is, the temporary date has a different month from the original. At that point, we know we've gone too far so we return the previous day number.