Applications, particularly business applications,
require additional methods for date manipulation than
provided with the Date
object. Here is a
script
to add 21 useful methods to the Date object constructor. Every
date object you create will inherit them.
The syntax for greating the methods is: Date.prototype.method_name = function () {[code];};
And, the methods are call like this: date_object.method_name().
Easy implementation and usage is what makes these worth while. Just add the download JavaScript file to your page and they are available for every date. Here is a list of the methods
Enter dates below and then click a method in which you are interested. An alert box will display the result. For some methods, additional information should be entered in the associated field. An explanation follows the demonstration.
This code takes advantage of JavaScript's object oriented architecture. Any object can be extended by assigning new properties and methods to the prototype property of the object's constructor. Properties and methods of the prototype property are shared by all instances of the object and not copied into each instance.
The first lines in the script add four constants to date objects. There are two arrays: one for the day names and one for month names. And there are two numeric constants: one having the milliseconds in a minute and the other holding the milliseconds in a day.
Date.prototype.DAYNAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; Date.prototype.MONTHNAMES = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; Date.prototype.msPERMIN = 1000 * 60; Date.prototype.msPERDAY = 1000 * 60 * 60 * 24;
The properties in the prototype are accessible
within each instance of a date using the key word
this.
And, accessible outside the instance with dot notation
as objectInstance.msPERMIN
.
The methods can be divided into three groups and
one odd method. Copy
is the odd method.
The others get information about the date,
add to the date, or get an interval between dates.
Copy: (not demonstrated) Makes a copy of the date. The need for this method may not be apparent, but it has to do with how Javascript handles assignment and passing of objects. Objects are assigned (or passed) by reference: a pointer to the original object is assigned. Only primative date types (string, number, boolean) are assigned by value. To copy a date, it first must be converted to a primative value, which is used as an agrument to the date constructor, and a new date instance created.
Method definition for copy
Date.prototype.copy = function () { return new Date(this.getTime()); };
Usage:
var old_date = new Date(); var new_copy = old_date.copy();
When a date object is assigned to another variable any changes made to the date referenced by the variable are reflected in the original date, because they are really the same date object. Copy, on the other hand creates a new date object passing the value of the original. Changes only affect one date object.
There are eight methods to retrieve information about a date.
These are: getCDay, getDayName, getCMonth, getMonthName,
getDayOfYear, and lastday.
getCDay: get three letter day abbreviation for a date.
Date.prototype.getCDay = function() { return this.getDayName().slice(0, 3); };
Usage:
var date = new Date(); var day_abbrev = date.getCDay();
getDayName: get full day name for a date.
Date.prototype.getDayName = function() { return this.DAYNAMES[this.getDay()]; };
Usage:
var date = new Date(); var string_day = date.getDayName();
getCMonth: get three letter month abbreviation for a date.
Date.prototype.getCMonth = function() { return this.getMonthName().slice(0, 3); };
Usage:
var date = new Date(); var month_abbrev = date.getCMonth();
getMonthName: get full month name for a date.
Date.prototype.getMonthName = function() { return this.MONTHNAMES[this.getMonth()]; };
Usage:
var date = new Date(); var string_month = date.getMonthName();
getDayOfYear: get the day of year for a date (1 through 365).
Date.prototype.getDayOfYear = function() { var start = new Date(this.getFullYear(), 0, 0); return this.getDaysBetween(start) * -1; };
Usage:
var date = new Date(); var day = date.getDayOfYear();
lastday: get number of days in month for a date.
Date.prototype.lastday = function() { var d = new Date(this.getFullYear(), this.getMonth() + 1, 0); return d.getDate(); };
Usage:
var date = new Date(); var num_days = date.lastday();
For the most part, these are not very remarkable functions,
but they can be convenient.
Little explanation is needed except to point out
the use of the key word this in the
method and that an anonymous
function is used
to assign the script to a property of the Date
constructor's prototype. This allows the usage
of date.method()
syntax for all dates.
lastday
is interesting because it sets a date to
the last day of a month by initializing it to
the zero ( 0 ) day of the following month.
Since zero is one day before the 1st, JavaScript
returns the date that is 1 day before the 1st or
the last day of the previous month.
JavaScript provides methods to convert a date
to a time string, but the format is based on the client settings.
Sometimes a specific format is required. to12HourString
and to24HourString
are for those occasions.
to12HourString: get time in 12 hour format (hh:mm:ss am/pm).
Date.prototype.to12HourString() { var h = this.getHours(); var m = "0" + this.getMinutes(); var s = "0" + this.getSeconds(); var ap; if (h >= 12) { ap = "pm"; if (h >= 13) h -= 12; } else { ap = "am"; if (h == 0) h = 12; } h = "0" + h; return h.slice(-2) + ":" + m.slice(-2) + ":" + s.slice(-2) + " " + ap; };
The code to retrieve a 12 hour time format with am/pm is more complex than the code for a 24 hour time. In either case, the digits are padded to two places. The only real process is padding left with a zero.
to24HourString: get time in 22 hour format (hh:mm:ss).
Date.prototype.to24hourString() { var h = "0" + this.getHours(); var m = "0" + this.getMinutes(); var s = "0" + this.getSeconds(); return h.slice(-2) + ":" + m.slice(-2) + ":" + s.slice(-2); };
Six methods, addDays, addWeeks, addMonths, addMonthsTrunc,
addYears, and addWeekDays
alter the date. Each method takes a numeric argument
and changes the date by adding (or subtracting in the
case of a negative argument).
addDays: add days to or subtract days from a date.
Date.prototype.addDays = function(d) { /* Adds the number of days to the date */ this.setDate( this.getDate() + d ); };
Usage:
var n = 10; var date = new Date(); date.addDays(n);
addWeeks (not demonstrated) add weeks to a date.
Date.prototype.addWeeks = function(w) { /* Adds the number of weeks to the date */ this.addDays(w * 7); };
Usage:
var weeks = 5; var date = new Date(); date.addWeeks(weeks);
addMonths: add months to a date.
Date.prototype.addMonths = function(m) { /* Adds the number of months to date. */ this.setMonth(this.getMonth() + m); };
Usage:
var n = 3; var date = new Date(); date.addMonths(n);
addMonthsTrunc: add months to a day adjusting month end.
Date.prototype.addMonthsTrunc = function(m) { // Adds the number of months to date and // doesn't go past last day when new month has // fewer days than the starting month. var d = this.getDate(); this.setMonth(this.getMonth() + m); /* Adjust when original month has more days then new month and overflows */ if (this.getDate() < d) this.setDate(0); };
Usage:
var n = 1; var date = new Date(2005, 1, 31); date.addMonthsTrunc(n);
addYears add years to a date.
Date.prototype.addYears = function(y) { // Adds the number of years to date. var m = this.getMonth(); this.setFullYear(this.getFullYear() + y); //Adjust for leap years if (m < this.getMonth()) { this.setDate(0); } };
Usage:
var n = -3; var date = new Date(); date.addYears(n);
addWeekDays: add business or week days to a date.
Date.prototype.addWeekDays = function(d) { /* Adds the necessary number of days * to the date to include the required * weekdays. */ var day = this.getDay(); //weekday number 0 through 6 var wkEnd = 0; //number of weekends needed var m = d % 5; //number of weekdays for partial week if (d < 0) { wkEnd = Math.ceil(d/5); //Yields a negative number of weekends switch (day) { case 6: //If staring day is Sat. one less weekend needed if (m == 0 && wkEnd < 0) wkEnd++; break; case 0: if (m == 0) d++; //decrease - part of weekend else d--; //increase - part of weekend break; default: if (m <= -day) wkEnd--; //add weekend if not enough days to cover } } else if (d > 0) { wkEnd = Math.floor(d/5); switch (day) { case 6: /* If staring day is Sat. and * no partial week one less day needed * if partial week one more day needed */ if (m == 0) d--; else d++; break; case 0: if (m == 0 && wkEnd > 0) wkEnd--; break; default: if (5 - day < m) wkEnd++; } } d += wkEnd * 2; this.addDays(d); };
Usage:
var n = 10; var date = new Date(); date.addWeekDays(n);
Again these are easy scripts for the most part.
The most interesting is addWeekDays
, but before
discussing that method let's quickly look at
the others
addDays
does exactly what you expect and there
are no surprises.
However, addMonths, addMonthsTrunc, and addYears
are
affected by months having different numbers
of days when the starting date is the last day
of the month. Javascript will adjust the date
when the day of month number is larger than the
number of days in the month by overflowing into
the next month. However, generally the desired result
is the last day of the month even if it is less than
the starting month; i.e., the 28th for February or 30th in those months
with only 30 days when the the starting day was the 31st.
addWeeks
was developed in response to an email request.
It simply multiplies the number of weeks by seven to get the days required
and then calling addDays
with those days.
The method addMonths
is affected by date overflow,
so adding one month to Jan 31st yields Mar 3rd (except in leap years when
it gives Mar 2nd).
This is adjusted in addMonthsTrunc
(for truncate)
and the extra days are truncated so adding
a month to Jan. 31st yields Feb. 28th
(unless, of course, the new date is a leap year
in which case it becomes Feb. 29th).
The method addYears
truncates the extra day
if the starting date is Feb. 29th and the new
date is not a leap year.
Many business functions depend on knowing
dates that include a certain number of days the
company is open for business or regular working days.
addWeekDays
adds the required number of weekdays
to the date. Basically, it calculates the number
of weekend days needed in the date
range, adds that to the required weekdays and
adds that number to the date.
It always produces a date between Monday
and Friday inclusive.
This does not take into account holidays or other special days the business is close. To account for holidays call addWeekDays in a loop that increases the required weekdays by one for every holiday in the date range from the starting date and the ending date. For example:
Example usage:
function due_date(bizdays) { var today = new Date(); var dueDate = null; var days = 0, holidays = 0; do { dueDate = today.copy(); days = bizdays + holidays; dueDate.addWeekDays(days); /* Your own lookup search * database or array for * holidays between today and dueDate * * holidays = query-results */ } while (days != bizdays + holidays); return dueDate; }
The function due_date
assumes that a date something is due
is a certain number of business days after the current date
and that there is a table either a database or and
array with the dates of all the holidays.
Also it assumes you have some way to query this table
and return the number of holidays that occur between two dates.
The loop starts by using a copy of the starting date. It adds the business days plus holidays, which is initially zero, to the starting date; thus getting an end date for the date range.
Your query is run getting the number of holidays between the start date and the date just calculated, due_date.
If the holidays plus the original business days equals the number of days used to calculate the due date your done. Otherwise, it resets the due date and tries again.
The last six methods work with two dates.
One is the object whose method is being
called, and the other is passed as an argument
to the method.
The methods, getDaysBetween, getWeekDays,
getMonthsBetween, getYearsBetween, and getAge
calculate the number
of days, weekdays, months, or years between two dates.
sameDayEachWeek
returns an array of the dates
for a particular day (e.g., Sunday or Monday, etc.)
in each week within a date range.
getDaysBetween get days between two dates. Updated to account for daylight savings and standard time.
Date.prototype.getDaysBetween = function(d) { //Returns number of days between to dates. var tmp = d.copy(); tmp.setUTCHours(this.getUTCHours(), this.getUTCMinutes(), this.getUTCSeconds(), this.getUTCMilliseconds()); var time = tmp.getTime() - this.getTime(); return time/this.msPERDAY; };
Usage:
var endDate = new Date(2005, 11, 28); var date = new Date(); var days = date.getDaysBetween(endDate);
getWeekDays: determine number of business or weekdays between two dates
Date.prototype.getWeekDays = function(d) { //Returns number of weekdays between to dates var wkEnds = 0, days = 0; var s = 0, e = 0; days = Math.abs(this.getDaysBetween(d)); if (days) { s = (d < this) ? d.getDay() : this.getDay() ; e = (d < this) ? this.getDay() : d.getDay(); wkEnds = Math.floor(days/7); if (s != 6 && s > e) wkEnds++; if (s != e && (s == 6 || e == 6) ) days--; days -= (wkEnds * 2); } return days; };
Usage:
var endDate = new Date(2005, 12, 5); var date = new Date(); var days = date.getWeekDays(endDate);
getMonthsBetween: determine number of months between two dates.
Date.prototype.getMonthsBetween = function(d) { //returns months between dates var d1 = this.getFullYear() * 12 + this.getMonth(); var d2 = d.getFullYear() * 12 + d.getMonth(); return d2 - d1; };
Usage:
var endDate = new Date(2005, 12, 5); var date = new Date(); var months = date.getMonthsBetween(endDate);
getMonthsBetween2: determine number of months between two dates adjusting for partial months. Updated to be more accurate and return two decimal places if results is not a whole number.
Date.prototype.getMonthsBetween2 = function(d) { var d1 = this.getFullYear() * 12 + this.getMonth(); var d2 = d.getFullYear() * 12 + d.getMonth(); var months = 0; var sDate, eDate, sign; var sDay, sLastDay, sAdj, eDay, eLast, eAdj, adj; if (this == d) { months = 0; } else if (d1 == d2) { //same year and month months = (d.getDate() - this.getDate())/this.lastday(); } else { if (d1 < d2) { sDate = this; eDate = d; sign = 1; } else { sDate = d; eDate = this; sign = -1; } sDay = sDate.getDate(); sLastDay = sDate.lastday(); eDay = eDate.getDate(); eLastDay = eDate.lastday(); if (sDay > eLastDay) { adj = eDay/eLastDay; } else { sAdj = sLastDay - sDay; eAdj = eDay > sLastDay ? sLastDay : eDay; adj = (sAdj + eAdj)/sLastDay; } months = Math.abs(d2 - d1) + (adj -1); if (months % 1 != 0 ) months = (months * sign).toFixed(2); else months = (months * sign); } return months; };
Usage:
var months = date.getMonthsBetween2(endDate);
getYearsBetween: determine the number of years between to dates.
Date.prototype.getYearsBetween = function(d) { //returns years and fraction between dates var months = this.getMonthsBetween(d); return months/12; };
Usage:
var endDate = new Date(2005, 12, 5); var date = new Date(); var days = date.getYearsBetween(endDate);
getAge: determine age from date of birth and current date.
Date.prototype.getAge = function(d) { if (!d) d = new Date(); return (this.getYearsBetween(d)); };
Usage:
var dateOfBirth = new Date(year-of-birth, month-of-birth, day-of-birth); var age = dateOfBirth.getAge();
The first two methods, getDaysBetween and getWeekDays, calculate the number of days between two dates. The first calculates the simple number of days. A negative number indicates that the date passed as an argument is earlier than the date whose method is being called. A copy of the date passed is used to avoid changing the original date. The time part of the copy is set to the time of the comparision date; thus eliminating the effect. So between Jan 1 23:59 and Jan 2 01:00 is 1 day even though it is only an hour. UTC time is used to eliminate the effect of timezones. Since the code is being run locally, the timezone adjustment only comes into play for locales that have daylight savings and standard time (summer and winter time changes).
getWeekDays
uses getDaysBetween
to calculate the number of days
between to dates and then it calculates how many weekend days
to subtract arriving at the number weekdays between the two dates.
This can then be further reduced by a count of holidays in that
date range.
The method getMonthsBetween
calculates the number
of months between two dates.
And, getYearsBetween
takes the result
of getMonthsBetween
and divides it by 12
to determine years and fraction of years.
The getAge
is just a wrapper function to create a specialized
case for getYearsBetween
. It defaults to the current date as
the end date.
sameDayEachWeek: determines number and dates of the same week day for each week between two dates.
Date.prototype.sameDayEachWeek = function (d, date) { /* Returns array of dates of same day each week in range */ var aDays = new Array(); var eDate, nextDate, adj; if (this > date) { eDate = this; nextDate = new Date(date.getTime()); } else { eDate = date; nextDate = new Date(this.getTime()); } /* Find when the first week day of interest occurs */ adj = (d - nextDate.getDay() + 7) %7; nextDate.setDate(nextDate.getDate() + adj); while (nextDate < eDate) { aDays[aDays.length] = new Date(nextDate.getTime() ); nextDate.setDate(nextDate.getDate() + 7); } return aDays };
Usage:
var endDate = new Date(2005, 12, 5); var date = new Date(); var listOfDays = date.sameDayEachWeek(2, endDate); //2 = Tuesday alert("Tuedays in the date range are:\n" + listOfDays.join("\n"));
sameDayEachWeek
was developed in response to an email request.
The developer needed a component that would determine the dates of a
particular day of week for each week within a date range.
One can imagine this used
in an application that was scheduling meetings on...say Tuesday...of
each week for the next two months.
The method takes a day-of-week value (0 through 6 for Sunday through Saturday) and a date object as arguments.
The first section of code calculates the earlier date: the argument or the date object calling the method. It creates a date to increment (nextDate) as a starting point and an end date. nextDate is a new date object so it can be changed without affecting the original dates.
Next we need to calculate the date the first week day of interest occurs. The tricky part is to be sure to subtract from the argument, d, before adding. Subtraction will cast a string to a number; whereas, addition will concatenate a number to a string having cast the number to a string. In some cases a string digit is likely to be passed so it needs to be cast to a number, which the subtraction will do, to get the correct results.
After that, a loop advances nextDate seven days each pass until the date is no longer in range. Each pass adds a new date object to the array. If nextDate were simply assigned to each array element, they would all be the same: the last date. This is because object assignment assigns a reference to the original so all elements would point to the same object. It is the same issue discussed in copying dates