473,695 Members | 2,404 Online

# Numeric Date Validation

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.

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.

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.

--
© John Stockton, Surrey, UK. ?@merlyn.demon. co.uk Turnpike v4.00 IE 4 ©
<URL:http://jibbering.com/faq/> Jim Ley's FAQ for news:comp.lang. javascript
<URL:http://www.merlyn.demo n.co.uk/js-index.htm> jscr maths, dates, sources.
<URL:http://www.merlyn.demo n.co.uk/> TP/BP/Delphi/jscr/&c, FAQ items, links.
Jul 23 '05 #1
30 3679
Dr John Stockton <sp**@merlyn.de mon.co.uk> writes:
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) ) }
I notice that you generate a new Array object for each call. You
could keep the date length array in variable and reuse it. It would
probably even be measureably faster (haven't tested though).
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.
Or if the month is invalid. How about:

var __months = [,31,28,31,30,31 ,30,31,31,30,31 ,30,31]
function DateOK2(Y, M, D) {
var ML = __months[M];
return D>0 && ML && (D<=ML ||
D==29 && Y%4==0 && (Y%100!=0 || Y%400==0) ) }

(M==2 is unnecessary, as ML && D>ML && D==29 implies M==2, but it
might be reasonable to keep it for readability).

Ok, a quick speed test in IE shows this version to be four times
faster than the above (which was only about twice as fast as
ValidDate, when testing only on February 29ths (not really fair :)).

The test Y%100>0 fails for Y<=0, because -96 % 100 == -96, not 4.
Change >0 to !=0 and that works.

It still fails for Y=0 for some reason. ... ah, probably because
Date maps Y=0 to year 1900, and your test doesn't. Since your
opponent is cheating, you win by default :)

/L
--
Lasse Reichstein Nielsen - lr*@hotpop.com
DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleD OM.html>
'Faith without judgement merely degrades the spirit divine.'
Jul 23 '05 #2
In article <c2************ **@merlyn.demon .co.uk>, Dr John Stockton
<sp**@merlyn.de mon.co.uk> wrote:
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.

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.

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.

Is the following correct for just a leap year check?

ayear is a 4 digit year.

var isLeapYear = ayear%4 == 0 && ayear%100 > 0 || ayear%400 == 0;

I always get confused on the sequence of evaluation (first && and then
|| unless parenthesis are used).

--
Dennis M. Marks
http://www.dcs-chico.com/~denmarks/
Replace domain.invalid with dcsi.net
-----= Posted via Newsfeeds.Com, Uncensored Usenet News =-----
http://www.newsfeeds.com - The #1 Newsgroup Service in the World!
-----== Over 100,000 Newsgroups - 19 Different Servers! =-----
Jul 23 '05 #3
Lasse Reichstein Nielsen wrote on 19 apr 2004 in comp.lang.javas cript:
Or if the month is invalid. How about:

var __months = [,31,28,31,30,31 ,30,31,31,30,31 ,30,31]
function DateOK2(Y, M, D) {
var ML = __months[M];
return D>0 && ML && (D<=ML ||
D==29 && Y%4==0 && (Y%100!=0 || Y%400==0) ) }

(M==2 is unnecessary, as ML && D>ML && D==29 implies M==2, but it
might be reasonable to keep it for readability).

Ok, a quick speed test in IE shows this version to be four times
faster than the above (which was only about twice as fast as
ValidDate, when testing only on February 29ths (not really fair :)).

Why this quest for speed?
Do you all want to test an enormous database for false entries?
Usually this test is for human interface validation only, I think,
so speed is not that important.

Personally, I would let the system do the thinking for me:

function dateOK(Y,M,D){
var da = new Date(Y,M-1,D)
return Y == da.getFullYear( ) &&
M-1 == da.getMonth() &&
D == da.getDate()
}

This also checkes for decimals, strings, negatives, etc.
correctly validating strings are allowed, like year '02004',month '3.00'
returns false if year<100

--
Evertjan.
The Netherlands.
Jul 23 '05 #4

Lasse Reichstein Nielsen wrote:

Dr John Stockton <sp**@merlyn.de mon.co.uk> writes:
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) ) }
I notice that you generate a new Array object for each call. You
could keep the date length array in variable and reuse it. It would
probably even be measureably faster (haven't tested though).
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.

Or if the month is invalid. How about:

var __months = [,31,28,31,30,31 ,30,31,31,30,31 ,30,31]
function DateOK2(Y, M, D) {
var ML = __months[M];
return D>0 && ML && (D<=ML ||
D==29 && Y%4==0 && (Y%100!=0 || Y%400==0) ) }

(M==2 is unnecessary, as ML && D>ML && D==29 implies M==2, but it
might be reasonable to keep it for readability).

Ok, a quick speed test in IE shows this version to be four times
faster than the above (which was only about twice as fast as
ValidDate, when testing only on February 29ths (not really fair :)).

The test Y%100>0 fails for Y<=0, because -96 % 100 == -96, not 4.
Change >0 to !=0 and that works.

Y should not be negative unless BCE dates, in which case you would add
4712, then perform the leapyear test (but only y % 4 == 0 -- this is why
the Julian calendar got so out of whack over the centuries)

It still fails for Y=0 for some reason. ... ah, probably because
Date maps Y=0 to year 1900, and your test doesn't. Since your
opponent is cheating, you win by default :)
The new Date object is only valid for the Gregorian calendar: Oct 15,
1582 and forward in time. The "full" year should always be used in the
new Date model. The year 0 is "mapped" to 1900 for "backward
compatibility" when the date object would accept 1 or 2 digit years
(pre-y2k). It really doesn't matter that Date supports 2-digit years
since the Gregorian calendar support in Date does not include (accurate
or correct) dates prior to Oct 15, 1582.

BTW -- there is *no* year 0 -- the calendar goes from 1 CE to 1 BCE
(going backwards). Year = 0 should be considered invalid (I don't
believe there is even a Day 0 in the Julian calendar -- Day 1 was Jan 1
4713 BCE [plus the Romans did not have a zero digit]).

/L
--
Lasse Reichstein Nielsen - lr*@hotpop.com
DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleD OM.html>
'Faith without judgement merely degrades the spirit divine.'

Jul 23 '05 #5
On Sun, 18 Apr 2004 15:56:39 -0700, Dennis M. Marks
<de******@domai n.invalid> wrote:

[snip]
Is the following correct for just a leap year check?
Not quite. You are correct that && is before ||, but that makes your
expression incorrect: || needs to be evaluated before && in this case.
Here's how the expression would be evaluated:

((( ayear % 4 ) == 0 ) && (( ayear % 100 ) > 0 )) || (( ayear % 400 ) ==
0 )

simplified to

( !( ayear % 4 ) && ( ayear % 100 )) || !( ayear % 400 )

Notice the difference between the one above, and mine (and Dr Stockton's)
below.
ayear is a 4 digit year.

var isLeapYear = ayear%4 == 0 && ayear%100 > 0 || ayear%400 == 0;

I always get confused on the sequence of evaluation (first && and then
|| unless parenthesis are used).

That's why I only remember one fact about any operator precedence table:
that parentheses are first. I'd write the line above:

var isLeapYear = ( !( ayear % 4 ) && (( ayear % 100 ) || !( ayear % 400
)));

It avoids any ambiguity, unless you can't read through all the parentheses.

Mike

--
Michael Winter
M.******@blueyo nder.co.invalid (replace ".invalid" with ".uk" to reply)
Jul 23 '05 #6

Dr John Stockton wrote:

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.prototyp e.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.getFullYea r().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 oneThirdIntoYea r = 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
*************** **
Jul 23 '05 #7
JRS: In article <18************ *************@d omain.invalid>, seen in
news:comp.lang. javascript, Dennis M. Marks <de******@domai n.invalid>
posted at Sun, 18 Apr 2004 15:56:39 :

Is the following correct for just a leap year check?

ayear is a 4 digit year.

var isLeapYear = ayear%4 == 0 && ayear%100 > 0 || ayear%400 == 0;

There are only four possible types of Gregorian Year Number :
Divisible by 400 e.g. 2000
Others Divisible by 100 e.g. 2100
Others Divisible by 4 e.g. 2104
Others e.g. 2105

It would not take long for you to test all four.

It is reasonable to assume that the precedences of the operators are the
same in all current browsers; OTOH, most programmers prefer parentheses
to doubt. Function Biss, in js-date4.htm, will retain its parentheses.

--
© John Stockton, Surrey, UK. ?@merlyn.demon. co.uk Turnpike v4.00 IE 4 ©
<URL:http://jibbering.com/faq/> Jim Ley's FAQ for news:comp.lang. javascript
<URL:http://www.merlyn.demo n.co.uk/js-index.htm> jscr maths, dates, sources.
<URL:http://www.merlyn.demo n.co.uk/> TP/BP/Delphi/jscr/&c, FAQ items, links.
Jul 23 '05 #8
JRS: In article <is**********@h otpop.com>, seen in
news:comp.lang. javascript, Lasse Reichstein Nielsen <lr*@hotpop.com >
posted at Mon, 19 Apr 2004 00:40:57 :
Dr John Stockton <sp**@merlyn.de mon.co.uk> writes:
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) ) }
I notice that you generate a new Array object for each call. You
could keep the date length array in variable and reuse it. It would
probably even be measureably faster (haven't tested though).

It trebles the speed for Feb 29, and quadruples it for commoner valid
days. But it is more tiresome to display in my js-date4.htm.

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.

Or if the month is invalid. How about:

var __months = [,31,28,31,30,31 ,30,31,31,30,31 ,30,31]
function DateOK2(Y, M, D) {
var ML = __months[M];
return D>0 && ML && (D<=ML ||
D==29 && Y%4==0 && (Y%100!=0 || Y%400==0) ) }

ISTM that, for almost all purposes, failure of validation should be a
rare event and means that a slow process, such as re-entering the data,
will be needed; therefore the speed of failure, unless unreasonable, is
only of minor importance. But the change is easy.

(M==2 is unnecessary, as ML && D>ML && D==29 implies M==2, but it
might be reasonable to keep it for readability).
Good.

Ok, a quick speed test in IE shows this version to be four times
faster than the above (which was only about twice as fast as
ValidDate, when testing only on February 29ths (not really fair :)).

The test Y%100>0 fails for Y<=0, because -96 % 100 == -96, not 4.
Change >0 to !=0 and that works.

It still fails for Y=0 for some reason. ... ah, probably because
Date maps Y=0 to year 1900, and your test doesn't. Since your
opponent is cheating, you win by default :)

Somewhere on my WWW site it says that, if the year may be small, then
D = new Date(0) ; D.setFullYear(Y , M, D)
is better than
D = new Date(Y, M, D)
also that new Date(0) is faster and safer than new Date() unless "now"
is needed.

Actually, Year 0 was not a leap year, and neither was Year 4, so it is
Year 4 that is wrong - the actual calendar, from -44 to +4, was the
Miscalculated Julian Calendar.
Unfortunately, it is now so fast that in order to compare its speed with
ValidDate directly in a reasonable time, the loop-counts must be varied.

I must think about a library function that uses setTimeout to time a
named routine for a given time ...
Another thought : if a Date Object is needed anyway, is checking as
above then creating better than creating and then checking it? If a
Date Object stores only ms-from-1970, then getMonth & getDate must
repeat work; but a smart Date Object could cache Y M D h m s ... In my
MSIE4,
DateOK(Y, M, D) ; X = new Date()
is twice as fast as
ValidDate(Y, M-1, D)

--
© John Stockton, Surrey, UK. ?@merlyn.demon. co.uk Turnpike v4.00 IE 4 ©
<URL:http://jibbering.com/faq/> Jim Ley's FAQ for news:comp.lang. javascript
<URL:http://www.merlyn.demo n.co.uk/js-index.htm> jscr maths, dates, sources.
<URL:http://www.merlyn.demo n.co.uk/> TP/BP/Delphi/jscr/&c, FAQ items, links.
Jul 23 '05 #9
In article <40************ ***@fxmahoney.c om>, Fox <fo*@fxmahoney. com>
writes

<snip>
The new Date object is only valid for the Gregorian calendar:
Date objects *implement* the Gregorian calendar.
Oct 15,
1582 and forward in time.
Are you saying the Pope's technical advisors weren't allowed to
calculate Gregorian dates before the Pope signed his proclamation making
them compulsory ? Rhubarb.

<snip>BTW -- there is *no* year 0 -- the calendar goes from 1 CE to 1 BCE
It's a matter of which representation of numbers you find it convenient
to display. For doing arithmetic modern binary numbers are more
convenient.

<snip>4713 BCE [plus the Romans did not have a zero digit]).

Back then no-one knew there was going to be a C to be B, so how can you
use BC numbers ?

Also, shouldn't you be writing MMMMDCCXIII (approx.).

John
--
John Harris
Jul 23 '05 #10

This thread has been closed and replies have been disabled. Please start a new discussion.