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.