Several year ago I decided to learn
Regular Expressions. I had been skimming over an article about using
regex for validation on web pages. It was at least a couple of weeks
before I sat down an read the article in full. After I did I wanted
to try to write a regex on my one but couldn't think of anything
original. Eventually I wrote a date regex that validated leap years
as well, something none of the other regex I had seen at the time
did. In the spirit of the article that introduced regular
expressions to me I that this was something that could be used with
Web Page validation. But at the time I wasn't doing a whole lot of
web page development. And what little web development I was doing,
didn't require me to use or validate dates.
I shared my regex on the web. I got a
lot of request to do other date formats. At first I was reluctant to
do that because my original pattern was troublesome to write, mostly
because the text editor I was using didn't help me match parenthesis
and I had a but of types to work out. Plus I always thought it
would be easier to reformat the date into the format the regex was
checking.
Eventually someone else reverse
engineer my regex into another format. Later I wrote patterns for
other formats as I saw uses beyond web page validation. I expanded
the patterns as I learn more about regex. For various reasons I
eventually moved away from working on these type of patterns. I had
done about all I could with them. Other than fixing a bug, generally
caused by a typo, I didn't do much with these patterns.
Recently I got another request for an
alternate format for one of my old patterns. I referred them to my
central depot of date regexes. But something else in the request
reminded me of my original idea of handling other formats. So I
decided to actually do it. So here it is in JavaScript
function isValidDate(ds, format) {
var reDate = /^(?!(?:10([-./])(?:0?[5-9]|1[0-4])\1(?:1582)))(?:(?:(?:(?:0?[13578]|1[02])(\/|-|\.)31)\2|(?:(?:0?[13-9]|1[0-2])(\/|-|\.)(?:29|30)\3))(?:(?!0000)\d{4})$|^(?:0?2(\/|-|\.)29\4(?:(?:(?:(?!000[04]|(?:(?:1[^0-6]|[2468][^048]|[3579][^26])00))(?:(?:(?:\d\d)(?:[02468][048]|[13579][26]))))|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:(?:0?[1-9])|(?:1[0-2]))(\/|-|\.)(?:0?[1-9]|1\d|2[0-8])\5(?:(?!0000)\d{4}))$/;
switch(format) {
case "dmy":
return reDate.test(ds.toString().replace(/(\d?\d)(\D)(\d?\d)(\D)(\d{4})/, "$3$2$1$4$5"));
case "ymd":
return reDate.test(ds.toString().replace(/(\d{4})(\D)(\d?\d)(\D)(\d?\d)/, "$3$2$5$4$1"));
default:
return reDate.test(ds);
}
}
That's it, The function returns true or false if the string passed is a valid date.
isValidDate("11/1/2011") // true
isValidDate("31/10/2011") // false
isValidDate("31/10/2011","dmy") // true
isValidDate("2031/10/20","ymd") // true
The big regex is the date
validator. I've talked about that enough in the past so I'm not
going to go into detail about how it works just trust me that it
does. Basics are 4-digits years, one or two digit months and days.
You have a choice of three date separators a peiod (.), a hyphen (-)
or a forward slash (/), Which ever one you chose you have to be
consistent. There have been a few modifications from my original date
regex to expand the time span it checks against and exclude missing
day from the switch from Julian to Gregorian in 1592. but most apps
won't push up against the old boundaries.
The default format checked is
dd/mm/yyyy
The function take a optional second
parameter that lets you specify other formats of yyyy-.mm-dd or
dd-mm-yyyy but passing “ymd” or “dmy” respectively.
If the second argument is passed the
given alternate format is converted to the default format for testing
by the use of one of two of simple regex replace operations. Nothing
really fancy The month, day and years are moved around to get to
mm-dd-yyyy format.
For this I just kept it simple and
handle the most common numerical date formats. For general webpage
validation this function is perfectly viable. You could have taken my
date regexes for all the various format and used each on base on the
format. The advantage of just using one date regex is maintenance is
easier. Not that the pattern needs any maintenance but if it did
there is only one complex regex to deal with instead of three. Also
with one regex you can't play around with other checks by using the
match. I did that while playing around with this but not including
that here. And while writing this I thought of a couple other things
I could build off of this.
Anyway there you have it. Three regexes, one scary two simple,
and a few lines of code to validate dates in 3 formats.