Regex for Gregorian Date Validation in Python


The challenge

Write a regular expression that validates the gregorian date in the format “DD.MM.YYYY”

Correct date examples:

"23.12.2008"
"01.08.1994"

Incorrect examples:

"12.23.2008"
"01-Aug-1994"
" 01.08.1994"

Notes:

  • maximum length of validator is 400 characters to avoid hardcoding. (shortest solution to date is 170 characters)
  • validator should process leap days (February, 29) correctly.
  • the date is Gregorian, it’s important to determine if year is leap: https://en.wikipedia.org/wiki/Gregorian_calendar

The solution in Python code

Option 1:

date_validator = (
    '((('
    '(0[1-9]|1\d|2[0-8])\.(0[1-9]|1[012])|'    # 01-28 of any month
    '(29|30)\.(0[13-9]|1[012])|'               # 29-30 of months, except February
    '(31\.(0[13578]|1[02])))\.'                # 31 of long months
    '([1-9]\d{3}|\d{3}[1-9]))|'                # any year, except 0000
    '(29\.02\.('                               # leap day
    '\d\d([2468][048]|[13579][26]|0[48])|'     # leap years (mod 4)   
    '([2468][048]|[13579][26]|0[48])00'        # leap years (mod 400)
    ')))$' )

Option 2:

not_0000  = "((?!0+$)\d{4})"
not_feb   = "(0[13-9]|1[0-2])"
div_4     = "([2468][048]|[13579][26]|0[48])"
day_31    = "(31\.(0[13578]|1[02]))"
day_29_30 = "(29|30)"
day_def   = "(0[1-9]|1\d|2[0-8])"
two_int   = "\d{2}"

day_29_30_31 = f"({day_31}|{day_29_30}\.{not_feb})\.{not_0000}"
day_default  = f"{day_def}\.(02|{not_feb})\.{not_0000}"
leap_year    = f"(29\.02\.({two_int}{div_4}|{div_4}00))"

date_validator = f"^({day_29_30_31}|{day_default}|{leap_year})$"

Option 3:

date_validator = r"^(((0[1-9]|1\d|2[0-8])\.(0[1-9]|1[0-2])|(29|30)\.(0[13-9]|1[0-2])|31\.(0[13578]|1[02]))\.(?!0000)\d{4}$)|29\.02\.(?!0000)(([02468][048]|[13579][26])00|\d{2}(0[48]|[2468][048]|[13579][26]))$"

Test cases to validate our solution

import re
test.assert_equals(bool(re.match(date_validator,'01.01.2009')), 
                    True, 'Basic correct date: 01.01.2009')
test.assert_equals(bool(re.match(date_validator,'01-Jan-2009')), 
                    False, 'Incorrect mask: 01-Jan-2009')
test.assert_equals(bool(re.match(date_validator,'05.15.2009')), 
                    False, 'Incorrect month: 15.15.2009')