Regular Expression to match a valid day in a date

^(((((((0?[13578])|(1[02]))[\.\-/]?((0?[1-9])|([12]\d)|(3[01])))|(((0?[469])|(11))[\.\-/]?((0?[1-9])|([12]\d)|(30)))|((0?2)[\.\-/]?((0?[1-9])|(1\d)|(2[0-8]))))[\.\-/]?(((19)|(20))?([\d][\d]))))|((0?2)[\.\-/]?(29)[\.\-/]?(((19)|(20))?(([02468][048])|([13579][26])))))$

From Expressions in category: Dates and Times

Validates the correct number of days in a month, looks like it even handles leap years.

You can of course change [\.\-/] with / to only allow slashes.


use DateTime;

Other solutions are fine, probably work, etc. Usually, you end up wanting to do a bit more, and then a bit more, and eventually you have some crazy code, and leap years, and why are you doing it yourself again?

DateTime and its formatters are your solution. Use them! Sometimes they are a bit overkill, but often that works out for you down the road.

my $dayFormat = new DateTime::Format::Strptime(pattern => '%d/%m/%Y');
my $foo = $dayFormat->parse_datetime($myDateString);

$foo is now a DateTime object. Enjoy.

If your date string wasn't properly formatted, $foo will be "undef" and $dayFormat->errstr will tell you why.


  • As many have noted above, if we want to validate the date as a whole then a RegEx is a very poor choice.
  • But if we want to match a pattern of numbers, in this case from 01-31 then RegEx is fine so long as there is some backend logic that validates the date as a whole, if so desired.
  • I see the expected answer currently fails for 10, 20.

    • Test: gawk 'BEGIN{ for(i=0;i<=32;i++){ if (i ~ /^([0-2]?[1-9]|3[01])$/){print i " yes"}else {print i " no"} } }
    • This can be corrected as follows: ^([0-2]?[1-9]|3[01]|10|20)$

So kindly consider the following solution...

1. Identify the sets that need to be matched:

  • Days with prefix "0": {01,...,09},{10,...,31}
    • Sub-set {10,...,31} can be split into => {10,...,29},{30,31}
  • Without any prefix: {1,...,31} => {1,...,9},{10,...,31}

2. Corresponding regular expressions for each sub-set:

---------------------------------
Sub-Set     |  Regular-Expression
---------------------------------
{01,...,09} | [0][1-9]
{10,...,29} | [1-2][0-9]
{30,31}     | 3[01]
{1,...,9}   | [1-9]
---------------------------------

Now we can group ([0][1-9]) and ([1-9]) together as ([0]?[1-9]). Where ? signifies 0 or 1 occurrences of the pattern/symbol. [UPDATE] - Thank you @MattFrear for pointing it out.

So the resulting RegEx is: ^(([0]?[1-9])|([1-2][0-9])|(3[01]))$

Tested here: http://regexr.com/?383k1 [UPDATE]


Regex for 0-31:

(0[1-9]|[12]\d|3[01])

Or if you don't want days with a preceding zero (e.g. 05):

([1-9]|[12]\d|3[01])