import re
regex = re.compile(r"""
(?(DEFINE)
(?# Date )
(?# Day ranges )
(?<d_day28>0[1-9]|1\d|2[0-8])
(?<d_day29>0[1-9]|1\d|2\d)
(?<d_day30>0[1-9]|1\d|2\d|30)
(?<d_day31>0[1-9]|1\d|2\d|3[01])
(?# Month specifications )
(?<d_month28>02)
(?<d_month29>02)
(?<d_month30>0[469]|11)
(?<d_month31>0[13578]|1[02])
(?# Year specifications )
(?<d_year>\d+)
(?<d_yearLeap>(?:\d*?(?:(?:(?!00)[02468][048]|[13579][26])|(?:(?:[02468][048]|[13579][26])00))|[48]00|[48])(?=\D|\b))
(?# Valid date formats )
(?<d_format>
(?&d_day28)\/(?&d_month28)\/(?&d_year)|
(?&d_day29)\/(?&d_month29)\/(?&d_yearLeap)|
(?&d_day30)\/(?&d_month30)\/(?&d_year)|
(?&d_day31)\/(?&d_month31)\/(?&d_year)
)
(?# Time )
(?# Time properties )
(?<t_period12>(?i)[ap]m|[ap]\.m\.(?-i))
(?# Hours )
(?<t_hours12>0\d|1[01])
(?<t_hours24>[01]\d|2[0-3])
(?# Minutes )
(?<t_minutes>[0-5]\d)
(?# Seconds )
(?<t_seconds>[0-5]\d)
(?# Milliseconds )
(?<t_milliseconds>\d{3})
(?# Valid time formats )
(?<t_format>
(?&t_hours12):(?&t_minutes):(?&t_seconds)(?:\.(?&t_milliseconds))?\ ?(?&t_period12)|
(?&t_hours24):(?&t_minutes):(?&t_seconds)(?:\.(?&t_milliseconds))?
)
(?# Datetime )
(?<dt_format>(?&d_format)\ (?&t_format))
)
\b(?&dt_format)\b
""", flags=re.MULTILINE | re.VERBOSE)
test_str = ("29/02/2016 23:10:59 - valid\n"
"29/02/2017 23:10:59 - invalid (not leap year)\n"
"30/03/2016 23:10:59 - valid")
matches = regex.finditer(test_str)
for match_num, match in enumerate(matches, start=1):
print(f"Match {match_num} was found at {match.start()}-{match.end()}: {match.group()}")
for group_num, group in enumerate(match.groups(), start=1):
print(f"Group {group_num} found at {match.start(group_num)}-{match.end(group_num)}: {group}")
Please keep in mind that these code samples are automatically generated and are not guaranteed to work. If you find any syntax errors, feel free to submit a bug report. For a full regex reference for Python, please visit: https://docs.python.org/3/library/re.html