Articles | Blind Date |
back
Blind Date
Introduction
This article demonstrates how to work with dates prior
to 1970 and after 1999 without using the inbuilt Date object. It will describe
how to pass this information to another frame or to another document.
'Features' (AKA bugs)
Some of the older versions of Netscape Navigator and
Internet Explorer have certain features or bugs around the date object.
For example the following script attempts to create and
display a date object for 1st February 1960, but using Netscape 2 this will
cause problems:
<SCRIPT LANGUAGE="JavaScript"><!--
var date = new Date(1960,1,1);document.write(date+'<BR>');
//--></SCRIPT>
|
The reason why this causes problems is that the inbuilt
Date object in the first release of JavaScript stores dates internally as the
number of milliseconds since January 1, 1970 00:00:00. The actual JavaScript
documentation states that:
'Dates prior to 1970 are not allowed.'
Other features with dates, is that sometimes dates
after 1999 also cause problems with older browsers. For example the following
script attempts to create and display a date object for 1st February 2000, but
using MSIE version 3, this may produce a negative value.
<SCRIPT LANGUAGE="JavaScript"><!--
var date = new Date(2000,1,1);document.write(date+'<BR>');
//--></SCRIPT>
|
The latest browsers do not have problems with dates
earlier than 1970 or later than 1999.
So how do we perform all of the items described at the
start of this article with only the following one-off use of the inbuilt Date
object?
var today = new Date();
var year = today.getYear();
var month = today.getMonth();
var day = today.getDate();
|
Simple - we number crunch!
The Gregorian Calendar
There are certain know facts about dates.
Arrays of Data
The most useful function contained within this example
is the following makeArray() function, which when passed an unspecified
number of arguments creates an array, with the first array element, i.e. [0],
containing the number of items within the array:
function makeArray() {
this[0] = makeArray.arguments.length;
for (i = 0; i<makeArray.arguments.length; i++)
this[i+1] = makeArray.arguments[i];
}
|
The makeArray() function is described in
detail in Arguments Array.
One simple use of the makeArray() function is
to create an array containing the months of the year, monthsofyear[]:
var monthsofyear = new makeArray('January','February','March',
'April','May','June',
'July','August','September',
'October','November','December');
|
Once we've used the makeArray() function once,
we can use it again and again and again:
var daysofmonth = new makeArray( 31, 28, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31);
var daysofmonthLY = new makeArray( 31, 29, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31);
|
The daysofmonth[] array contains the number of
days for each month from January through to December. Whereas the daysofmonthLY[]
array contains the number of days in a Leap Year for each month from January
through to December.
To retrieve the information stored within these arrays
is straight forward:
for (var i=1; i<=12; i++)
document.write(monthsofyear[i] + ' ' +
daysofmonth[i] + ' ' +
daysofmonthLY[i] +
'</BR>');
|
Which when run produces:
January 31 31
February 28 29
March 31 31
April 30 30
May 31 31
June 30 30
July 31 31
August 31 31
September 30 30
October 31 31
November 30 30
December 31 31
Selecting Dates
How do we allow the user to enter valid dates, for
example 28/1/1900 or 4/01/1965? Well one way would be to allow the user to type
in a date in a text box along the lines of:
But, what if the user typed in the date in one of the
following formats: mm/dd/yyyy, m/d/yy or yyyy-mm-dd.
It would be extremely difficult, and highly error
prone, to vet and validate all possible date combinations that the user could
enter.
I prefer the following method which restricts the input
of a date to a well defined structure:
So, how did we produce this form, how did the current
date get selected by default, and how do we retrieve the date selected?
The inputDateForm form was simply displayed
using the following code:
<FORM NAME="inputDateForm"
onSubmit="return ChosenDate(document.inputDateForm);">
Day: <SELECT NAME="day">
<SCRIPT LANGUAGE="JavaScript"><!--
document.write(dayOutput);
//--></SCRIPT>
</SELECT>
Month: <SELECT NAME="month">
<SCRIPT LANGUAGE="JavaScript"><!--
document.write(monthOutput);
//--></SCRIPT>
</SELECT>
Year:
<SCRIPT LANGUAGE="JavaScript"><!--
document.write(yearOutput);
//--></SCRIPT>
<INPUT TYPE="SUBMIT" VALUE="Show">
</FORM>
|
The three JavaScript variables dayOutput, monthOutput
and yearOutput were prepared slightly earlier using the following code.
function y2k(number) { return (number < 1000)
? number + 1900 : number; }
function padout(number) { return (number < 10)
? '0' + number : number; }
var today = new Date();
var thisYear = y2k(today.getYear());
var thisMonth = today.getMonth()+1;
var thisDay = today.getDate();
var dayOutput = '';
for (var days=1; days <= 31; days++) {
if (days == thisDay)
dayOutput += '<OPTION VALUE="' + padout(days) + '" SELECTED>
' + days;
else
dayOutput += '<OPTION VALUE="' + padout(days) + '">' + days;
}
var monthOutput = '';
for (var months=1; months <=12; months++) {
if (months == thisMonth)
monthOutput += '<OPTION VALUE="' + padout(months) + '" SELECTED>
' + monthsofyear[months];
else
monthOutput += '<OPTION VALUE="' + padout(months) + '">
' + monthsofyear[months];
}
var yearOutput = '<INPUT TYPE="TEXT" NAME="year" SIZE="4" VALUE="
' + thisYear + '">';
|
The date today is obtained using the inbuilt
Date object. The year is obtained using getYear() and is then converted
to CCYY using the y2k() function.
The dayOutput variable contains the HTML
required to display 31 options, one for each day of a calendar month. The option
that corresponds with the current thisDay has the addition of a SELECTED
property.
The monthOutput variable contains the HTML
required to display 12 options, one for each month of the year. It utilises the monthsofyear[]
array defined earlier in this article. Again the option that corresponds with
the current thisMonth has the addition of a SELECTED property.
The padout() function is used to pad out
values less than 10 with an extra leading zero, i.e. 5 becomes 05.
This is helpful, when we later pass the data between documents using the
locations search property.
The yearOutput variable, works slightly
differently in that in contains the HTML required to display a form text field
with the current year as its value.
To retrieve the date selected we use the onSubmit event handler of the inputDateForm
forms <FORM> tag to invoke the the following ChosenDate()
function with a reference to the documents inputDateForm form:
function ChosenDate(object) {
year = object.year.value;
month = object.month.options[object.month.selectedIndex].value;
day = object.day.options[object.day.selectedIndex].value;
alert('Date: '+day+'/'+month+'/'+year);
return false;
}
|
The reference to the documents inputDateForm
is received as object, which is then used to retrieve the value
of the year text box and the month and day options
using the selectedIndex property.
Finally the ChosenDate() function returns false
to the forms onSubmit event handler to halt the completion of the form
submission.
Valid-dating, or, Validating the Date
"But.." I hear you say, "But, not every
month has 31 days. The user could still select an invalid date!"
As the code stands now, yes, they could. However, if we
change the ChosenDate() function as follows, then all is not lost.
function LeapYear(year) {
if ((year/4) != Math.floor(year/4)) return false;
if ((year/100) != Math.floor(year/100)) return true;
if ((year/400) != Math.floor(year/400)) return false;
return true;
}
function ValidDate(day,month,year) {
if ( (LeapYear(year) && (day > daysofmonthLY[month])) ||
(!LeapYear(year) && (day > daysofmonth[month])) )
return false;
else
return true;
}
function ChosenDate(object) {
year = object.year.value - 0;
month = object.month.options[object.month.selectedIndex].value - 0;
day = object.day.options[object.day.selectedIndex].value - 0;
if (!ValidDate(day-0,month-0,year-0)) {
alert('You must enter a valid date');
return false;
}
alert('Valid Date: '+day+'/'+month+'/'+year);
return false;
}
|
We must first convert the day, month
and year values from strings to numbers, by subtracting zero from them,
before passing the date to the ValidDate() function. The option values
within forms are always string values.
The ValidDate() function checks the number of
days in the month selected by comparing it against the relevant entry in the daysofmonth[]
or in the daysofmonthLY[] array if its a Leap Year by passing the data
to the ValidDate() function.
To determine whether or not its a leap year, we pass
the year to the LeapYear() function.
If the date is not valid (i.e. ! reverses the
value returned from the ValidDate() function), then we display an error
message.
Passing the date upwards
Its possible, once we have validated the date, to pass
it up to the parent document, for use by another frame.
For example, the parent document could define the three
variables year, month and day, along with the
required frameset:
<HTML><HEAD>
<SCRIPT LANGUAGE="JavaScript"><!--
function y2k(number) { return (number < 1000)
? number + 1900 : number; }
var today = new Date();
var year = y2k(today.getYear());
var month = today.getMonth()+1;
var day = today.getDate();
var thisYear = year;
var thisMonth = month;
var thisDay = day;
//--></SCRIPT>
<FRAMESET ROWS="130,*">
<FRAME SCROLLING=NO FRAMEBORDER=
0 BORDER=0 NORESIZE SRC="input.htm">
<FRAME SCROLLING=YES FRAMEBORDER=0 BORDER=
0 NORESIZE SRC="blank.htm" NAME="outputFrame">
</FRAMESET>
</HEAD></HTML>
|
The blank.htm document just holds the
following HTML:
The input.htm document could then hold all the
JavaScript code described in this article so far, but with an amended ChosenDate()
function as follows:
function ChosenDate(object) {
year = object.year.options[object.year.selectedIndex].text - 0;
month = object.month.options[object.month.selectedIndex].value - 0;
day = object.day.options[object.day.selectedIndex].value - 0;
if (!ValidDate(day,month,year)) {
alert('You must enter a valid date');
return false;
}
parent.year = year;
parent.month = month;
parent.day = day;
parent.outputFrame.location.href = 'output.htm';
return false;
}
|
This amended ChosenDate() function changes the
value of the parent variables, before changing the location of
the outputFrame from blank.htm to output.htm.
The output.htm can simple retrieve the parent
variable values with the following code, which displays the date retrieved from
the parent document along with the current date:
<HTML>
<HEAD>
<SCRIPT LANGUAGE="JavaScript"><!--
function y2k(number) { return (number < 1000)
? number + 1900 : number; }
var year = parent.year - 0;
var month = parent.month - 0;
var day = parent.day - 0;
var today = new Date();
var thisYear = y2k(today.getYear());
var thisMonth = today.getMonth()+1;
var thisDay = today.getDate();
//--></SCRIPT>
</HEAD>
<BODY>
<SCRIPT LANGUAGE="JavaScript"><!--
document.write('Valid Date: '+day+'/'+month+'/'+year+'<BR>');
document.write('Todays Date: '+thisDay+'/'+thisMonth+
'/'+thisYear+'<BR>');
//--></SCRIPT>
</BODY>
</HTML>
|
Passing the date onwards
Its also possible, once we have validated the date, to
pass it on to another document. If we include an ACTION property to the
existing inputDataForm, so that if the form is submitted it loads the output.htm
document:
<FORM NAME="inputDateForm" ACTION="output.htm"
onSubmit="return ChosenDate(document.inputDateForm);">
|
Up till now we have always returned false from
the ChosenDate()function. To enable the ACTION property on the
inputDataForm to load the next document we need to return true
from the ChosenDate() function:
function ChosenDate(object) {
year = object.year.value - 0;
month = object.month.options[object.month.selectedIndex].value - 0;
day = object.day.options[object.day.selectedIndex].value - 0;
if (!ValidDate(day,month,year)) {
alert('You must enter a valid date');
return false;
}
if (parent.location.href != window.location.href) {
parent.year = year;
parent.month = month;
parent.day = day;
parent.outputFrame.location.href = 'output.htm';
}
else {
return true;
}
return false;
}
|
The above amended ChosenDate() function, tests
whether the current document is loaded within a Frameset by comparing the location
of the parent document with the location of the current window.
If they are not equal (i.e. !=) then, as
before, the variables in the parent document are updated and the location
of the outputFrame is changed to load the output.htm document.
Otherwise the ChosenDate() function returns true.
The output.htm document can now be invoked as
a frame within a Frameset, or as a document on its own:
<HTML>
<HEAD>
<SCRIPT LANGUAGE="JavaScript"><!--
function y2k(number) { return (number < 1000)
? number + 1900 : number; }
if (location.search.length == 0) {
var year = parent.year - 0;
var month = parent.month - 0;
var day = parent.day - 0;
}
else {
var day = location.search.substring(5,7) - 0;
var month = location.search.substring(14,16) - 0;
var year = location.search.substring(22) - 0;
}
var today = new Date();
var thisYear = y2k(today.getYear());
var thisMonth = today.getMonth()+1;
var thisDay = today.getDate();
//--></SCRIPT>
</HEAD>
<BODY>
<SCRIPT LANGUAGE="JavaScript"><!--
document.write('Valid Date: '+day+'/'+month+'/'+year+'<BR>');
document.write('Todays Date: '+thisDay+'/'+thisMonth+
'/'+thisYear+'<BR>');
//--></SCRIPT>
</BODY>
</HTML>
|
The above amended output.htm now checks the length
of any possible search string that may have been passed to it. For
example, if the date chosen on the previous input.htm document had been
the 4th of January 1965, then the search string would hold:
?day=04&month=01&year=1965
The text items day, month and year
are the named form elements from the inputDataForm on the previous
document.
Using the substring() method we are able to
extract the values of there form fields, 04, 01 and 1965.
If the current location's search
string is empty (i.e. length is zero) then we assume the variable
values are to be retrieved from the
parent document.
Working Example
Why not try out the frame
version?
Source Code
You can view the source code of the four components:
Articles | Blind Date |
back
|