Dr said:
It has appeared that ancient sources give a method for Numeric Date
Validation that involves numerous tests to determine month length;
versions are often posted by incomers here. That sort of code seems
unnecessarily long.
For some while, the following approach has been given here :-
function ValidDate(y, m, d) { // m = 0..11 ; y m d integers, y!=0
with (new Date(y, m, d))
return (getMonth()==m && getDate()==d) }
and it may remain the shortest code. But it does require, in every
case, the creation and disposal of a Date Object.
That depends on your point of view and what you consider a valid date
entered by a user (and it *must* be entered by a user, otherwise *why*
would you need to validate your own code?)
The Date object has very W I D E latitude in what it considers a valid
date and it is usually *best* to let the language deal with itself.
At it's most simple, a valid date is the following:
Date.prototype.valid = function()
{
return !(this == "Invalid Date" || isNaN(this));
}
This routine handles gecko and M$ versions of the Date object and
assures that the values under test will produce a valid Date object
instance. E.g.:
(new Date("howdy")).valid() => false
As to your concern for "the shortest code" for a validation script: No
validation code for user input (in html) is ever "time-critical" - so
whatever it takes to get the job done -- anything under 1/30th of a
second is faster than most humans can perceive...and the difference
between the "old way" and your "new proposal" can be measured in
milliseconds (probably less than 50). Unless you have to process
thousands of dates...well, I seriously doubt you'd find anyone with the
patience to spend on that amount of data entry to quantify the gain in efficiency.
Your concern about "disposal" of Date objects is non sequitur. It is
automatic in JavaScript and is not something we have the possibility of
having direct access to. Simply exiting a function in which a Date is
created as a local variable will cause automatic disposal. For global
Date objects, simply reuse as few as minimally required, or set the
object to null when through with them in order to "coerce" automatic
garbage disposal. The "timing" of the disposal cannot be guaranteed.
The following is about 50% longer in code, but about four times faster
in my system - and it seems to be right, too.
function DateOK(Y, M, D) {
return D>0 && (D<=[,31,28,31,30,31,30,31,31,30,31,30,31][M] ||
D==29 && M==2 && Y%4==0 && (Y%100>0 || Y%400==0) ) }
Note that checking for 1 <= M <= 12 is inherent, and that the Leapness
of the year is only determined if the date given is February 29th.
for leapyears, the Date object will correctly return Feb 29 data --
considering the date is already valid (that is, not "Invalid Date" in
gecko or NaN in IE) for the 60th day into a leapyear, otherwise Mar 1.
If the information about leapyear is important, then 1) if all that's
needed is the year:
Number.prototype.leapyear = function()
{
return (this % 4 == 0 && this % 100 != 0) || this % 400 == 0;
}
e.g.: year.leapyear() or (2004).leapyear()
or 2) from a Date instance:
Date.prototype.isLeapYear = function()
{
return this.getFullYear().leapyear(); // so years can be tested both ways
}
// this is good from 1582 until the end of the Gregorian Calendar
As mentioned, the Date Object offers considerable latitude for valid
data entry. Why allow it? Simplicity. For instance, to retrieve the
180th day of a year:
var day180 = new Date(year, 0, 180);
var oneThirdIntoYear = new Date(year, 0, (year.leapyear() ? 366 :
365)/3); // May 1
// the Date Object automatically converts the fractional part to an
integer value
the last day of June is:
var lastofJune = new Date(year, 6, 0); // where july = 6 => returns June
30 w/day of week, etc...
[ also, by the same token:
var isLeapYear = (new Date(year, 2, 0)).getDate() == 29;
// or new Date(year, 1, 29).getMonth() == 1; ]
10000 days from 6/1/2004:
new Date(2004, 5, 1 + 10000);
These are all valid Date representations. Filtered through "ValidDate"
they are "out of range" and disqualified.
The following are also valid (in Date):
var m = null;
var d = null;
var y = 2004;
new Date(y, m, d) => Dec 31 2003
m = 11;
d = 1;
y = null;
new Date(y, m, d) => Dec 1 1900 [*see my note to Lasse about year=0]
[null values are equivalent to 0 in Date]
Also, it is easy to use only the quadrennial rule if it is certain that
dates are in 1901-2099, or only two rules for 2001-2399.
as far as I can tell, the latest versions of JS have implemented correct
Gregorian Dates (including leapyear for 1600). Dates before Oct 15, 1582
seem to be incorrect [Oct 5-14 1582 should not exist, but they do in
gecko and IE and Oct 4 1582 shows Mon -- it should be Thu]. So why
restrict the formula and remove the generality when it can be used
throughout the entire range of dates the Date object is now capable of representing?
ValidDate(12004, 2, 1) is a valid date, yet still meaningless in most
contexts (barring astronomical calculations.) Validating a date entry
from a form by a user is basically an exercise in catching typos,
otherwise, an example like this defeats both our purposes - we still
need to rely on the intrisic scrupulousness of the serious user. Anyone
else "bent" on providing meaningless values can pretty much easily do so
[unless the validation is much more strict in its limits, e.g., credit
card expiration date validation -- from "this month" to about 5 years hence...]
if you use the Date object's approach to "month management" (zero-based
months) you can reduce the days array to:
var days = [31,28,31,30,31,30,31,31,30,31,30,31];
Fox
*****************