Regular Expression | Leap Years and More

I've recently been looking for a regular expression to do some client side date checking, and I haven't been able to find one that can satisfy the following criteria:

  • Has a range from 1800 - Now
  • Performs proper date checking with leap years
  • MM/DD/YYYY Form
  • Invalid Date Checking

(These constraints were outside of my scope and are a requirement as per the client, despite my efforts to convince them this wasn't the best route)

Current code:

$('input').keyup(function()
{
       var regex = /^(?:(0[1-9]|1[012])[\/.](0[1-9]|[12][0-9]|3[01])[\/.](18|19|20)[0-9]{2})$/;
       $(this).toggleClass('invalid',!regex.test($(this).val()));    
});

Update:

I should note that this is primarily to see if a regular expression like this would be possible (as the use of a Regex is not my choice in this matter). I am aware of the other (and better) options for validating a date, however as previously mentioned - this is to see if it was possible through a regular expression.


As is mentioned elsewhere, regular expressions almost certanily not what you want. But, having said that, if you really want a regular expression, here is how it is built:

31 day months

(0[13578]|1[02])[\/.](0[1-9]|[12][0-9]|3[01])[\/.](18|19|20)[0-9]{2}

30 day months

(0[469]|11)[\/.](0[1-9]|[12][0-9]|30)[\/.](18|19|20)[0-9]{2}

February 1-28 always valid

(02)[\/.](0[1-9]|1[0-9]|2[0-8])[\/.](18|19|20)[0-9]{2}

February 29 also valid on leap years

(02)[\/.]29[\/.](((18|19|20)(04|08|[2468][048]|[13579][26]))|2000)

which means it would be this if you put it all together:

((0[13578]|1[02])[\/.](0[1-9]|[12][0-9]|3[01])[\/.](18|19|20)[0-9]{2})|((0[469]|11)[\/.](0[1-9]|[12][0-9]|30)[\/.](18|19|20)[0-9]{2})|((02)[\/.](0[1-9]|1[0-9]|2[0-8])[\/.](18|19|20)[0-9]{2})|((02)[\/.]29[\/.](((18|19|20)(04|08|[2468][048]|[13579][26]))|2000))

This version is a little shorter, but a little harder to understand.

((0[13578]|1[02])[\/.]31[\/.](18|19|20)[0-9]{2})|((01|0[3-9]|1[1-2])[\/.](29|30)[\/.](18|19|20)[0-9]{2})|((0[1-9]|1[0-2])[\/.](0[1-9]|1[0-9]|2[0-8])[\/.](18|19|20)[0-9]{2})|((02)[\/.]29[\/.](((18|19|20)(04|08|[2468][048]|[13579][26]))|2000))

These scripts are long and unmaintainable. It should be clear that this isn't a good idea, but it is possible.

Caveats:

  • range 1800-2099 (more can be added without too much difficulty, but requires changes in 4-6 disparate places)
  • requires 2 digit months and days (the strictness could be removed from the expression in ~8 places)
  • [\/.] as seperators (8 places)
  • Hasn't been tested (we could check it against all digit combinations and compare with the javascript date function? [proof that we're reinventing the wheel])

I would suggest that you abandon the attempt to use regular expressions for this. You're much better off parsing the date into its constituent parts (month, day, year), and then using numerical comparisons to make sure it's in the proper range.

Better yet, see if the Javascript Date.parse function will do what you want.

Parsing dates with regular expressions is possible, but frustrating. It's hard to get right, the expression is difficult for non-regex wizards to understand (which means it's difficult to prove that the thing is correct), and it is slow compared to other options.