Reputation: 6533
I have the following regex date validator but it is allowing the following date:
"99/99/2012"
Just not sure why, if anyone knows how I can change the regex to check for above
Date format is following : 06/05/2012 mm/dd/yyyy
Regex:
^(((0?[1-9]|1[012])/(0?[1-9]|1\d|2[0-8])|(0?[13456789]|1[012])/(29|30)|(0?[13578]|1[02])/31)/(19|[2-9]\d)\d{2}|0?2/29/((19|[2-9]\d)(0[48]|[2468][048]|[13579][26])|(([2468][048]|[3579][26])00)))$
Upvotes: 4
Views: 3990
Reputation: 30715
I'm seeing a lot of overcomplication in these patterns. Your date format is actually pretty simple. I think you're taking the wrong approach by ignoring the overlap between a 28-day month and a 31-day month.
Before I explain any further, let me point out that unless you're stuck with regex, there are better ways to parse and validate dates. The best option is DateTime.TryParseExact, which is made for this. If you're using WebForms, you can just drop in a CompareValidator with Operator="DataTypeCheck"
and Type="Date"
.
Now that I've said the magic words, let's talk regex patterns.
Change your way of looking at this. It's true that not all months contain 31 or even 30 days, but how many contain 28 days?
...All of them! So if you want to match April and May, you don't need to cover April 1-30 and then May 1-31. You can write a pattern to cover the first 30 days of either month, then hit May 31 by itself. The result is something like [45]/([012]?[1-9]|[123]0)|5/31
, which is half as long as it would have been otherwise.
Consider this pattern, expanded for clarity:
^(
( #First, we'll cover months and days for a normal year. Forget leap years
#for a second.
(
(?<month>0?[1-9]|1[012]) #First we account for up to 28 days,
/(?<day>[01]?[1-9]|10|2[0-8]) #which ALL months have.
)
|
(
(?<month>0?[13-9]|1[012]) #Every month but February has at
/(?<day>29|30) #least 30 days, so cover that.
)
|
(
(?<month>0?[13578]|1[02]) #Cover the 31st day for months
/(?<day>31) #that have one.
)
)
/(?<year>(1[89]|20)[0-9]{2}) #Any year between 1800 and 2099.
| #Normal years: Done. Now we just need to cover February 29,
#and only for leap years.
(?<month>0?2)
/(?<day>29)
/(?<year>
(1[89]|20) #Century doesn't matter, since 100 is divisible by 4.
(
[24680][048] #If the decade is even, leap years end in [048].
|
[13579][26] #If the decade is odd, leap years end in 2 or 6.
)
)
)$
And we're done. Enforces mm/dd/yyyy format, validates for existing dates, and it's short and easy to read. Here's the minified version:
^(((0?[1-9]|1[012])/([01]?[1-9]|10|2[0-8])|(0?[13-9]|1[012])/(29|30)|(0?[13578]|1[02])/31)/(1[89]|20)[0-9]{2}|0?2/29/(1[89]|20)([24680][048]|[13579][26]))$
Since this is .NET, you can use lookarounds to shorten it up quite a lot:
^(
(?!(0?[2469]|11)/31)
(?!0?2/(29|30))
(?<month>0?[1-9]|1[012])
/
(?!0?0/)
(?<day>[012]?[0-9]|3[01])
/
(?<year>(1[89]|20)[0-9]{2})
|
(?<month>0?2)/(?<day>29)
/
#Years ending in 00 are only leap years if they're divisible by 400:
(?!1[89]00)
(?<year>(1[89]|20) ( [24680][048] | [13579][26] ) )
)$
Upvotes: 3
Reputation: 12880
Just throwing out another option to consider...
I like simple regular expressions but the crazy ones are tough to validate and maintain.
Could you instead write your own validator and use DateTime.TryParseExact()
to do the work?
See http://msdn.microsoft.com/en-us/library/ms131044.aspx for further details
(untested)
// this is inside a custom validator
DateTime d;
// get dateString from the control
if (DateTime.TryParseExact(dateString,
"MM/dd/yyyy",
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out d)
{
// valid dateString
}
else
{
// invalid dateString
}
Upvotes: 2
Reputation: 9030
You can use this regex:
((^(10|12|0?[13578])([/])(3[01]|[12][0-9]|0?[1-9])([/])((1[8-9]\d{2})|([2-9]\d{3}))$)|(^(11|0?[469])([/])(30|[12][0-9]|0?[1-9])([/])((1[8-9]\d{2})|([2-9]\d{3}))$)|(^(0?2)([/])(2[0-8]|1[0-9]|0?[1-9])([/])((1[8-9]\d{2})|([2-9]\d{3}))$)|(^(0?2)([/])(29)([/])([2468][048]00)$)|(^(0?2)([/])(29)([/])([3579][26]00)$)|(^(0?2)([/])(29)([/])(1[89][0][48])$)|(^(0?2)([/])(29)([/])([2-9][0-9][0][48])$)|(^(0?2)([/])(29)([/])(1[89][2468][048])$)|(^(0?2)([/])(29)([/])([2-9][0-9][2468][048])$)|(^(0?2)([/])(29)([/])(1[89][13579][26])$)|(^(0?2)([/])(29)([/])([2-9][0-9][13579][26])$))
A bit intimidating, yes, but I've used it with great success for many years.
You can test it using this online expression tester.
Enjoy!
Upvotes: 2