I see a lot of difficulties in these patterns. The format of your date is actually quite simple. I think you are mistaken in ignoring the overlap between the 28-day month and the 31-day month.
Before I explain again, let me point out that if you are not stuck in a regular expression, there are more efficient ways to parse and check dates. The best option is DateTime.TryParseExact , which is made for this. If you use WebForms, you can simply add CompareValidator with Operator="DataTypeCheck" and Type="Date" .
Now that I have said the magic words, let me talk about regex patterns.
Change your outlook on this. True, not all months contain 31 or even 30 days, but how many of them contain 28 days?
... All of them! So, if you want to match April and May, you do not need to cover April 1-30, and then May 1-3. You can write a sample covering the first 30 days of any month, and then May 31 on its own. The result is something like [45]/([012]?[1-9]|[123]0)|5/31 , which is half as much as otherwise.
Consider this template extended 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 you're done. Provides the format mm / dd / yyyy, checks existing dates, and its brevity and is easy to read. Here is the version given:
^(((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]))$
Edit:
Since this is .NET, you can use lookarounds to shorten it pretty much:
^( (?!(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] ) ) )$