0

I've googled up a lot of regexes to validate dates in DD.MM.YYYY format. Like this one:

(0[1-9]|1[0-9]|2[0-8]|(?:29|30)(?!.02)|29(?=.02.\d\d(?:[02468][048]|[13579][26]))|31(?=.0[13578]|.1[02]))(?:\.(?=\d\d\.)|-(?=\d\d-)|\/(?=\d\d\/))(0[1-9]|1[0-2])[.\/\-]([1-9][0-9]{3})

and it works fine.

As far as I understand the ([1-9][0-9]{3}) part refers to year. I tried removing it and it started validating dates ending with dots, like 01.05., 10.07. etc.

>>> regex = '^(0[1-9]|1[0-9]|2[0-8]|(?:29|30)(?!.02)|29(?=.02.\d\d(?:[02468][048]|[13579][26]))|31(?=.0[13578]|.1[02]))(?:\.(?=\d\d\.)|-(?=\d\d-)|\/(?=\d\d\/))(0[1-9]|1[0-2])[.\/\-]$'
>>> aaa = '12.02.'
>>> bbb = '32.02.'
>>> print(re.match(regex, aaa))
<_sre.SRE_Match object; span=(0, 6), match='12.02.'>
>>> print(re.match(regex, bbb))
None

But when I remove the part that takes care of the dot/dash divider

[.\/\-]

it doesn't validate dates without the trailing dots:

>>> regex = '^(0[1-9]|1[0-9]|2[0-8]|(?:29|30)(?!.02)|29(?=.02.\d\d(?:[02468][048]|[13579][26]))|31(?=.0[13578]|.1[02]))(?:\.(?=\d\d\.)|-(?=\d\d-)|\/(?=\d\d\/))(0[1-9]|1[0-2])$'
>>> aaa = '12.02'
>>> bbb = '32.02'
>>> print(re.match(regex, aaa))
None
>>> print(re.match(regex, bbb))
None

How do I make this work?

UPDATE ABOUT FEB 28 / FEB 29:

It's okay if it won't validate 28/29 Feb, this is acceptable in my case.

UPDATE ABOUT PYTHON:

I cannot use python validation for this, sadly it's only a regex field in a web form that I can use.

kurtgn
  • 8,140
  • 13
  • 55
  • 91
  • IMHO, using a regex like that to validate a date is wrong. Better to split the input string by the period separator and then checking that the year/month/date is valid. Maybe it's more lines of code but it will surely be more readable. – vainolo Sep 28 '16 at 19:21
  • Possible duplicate of [How to validate a date?](http://stackoverflow.com/questions/5812220/how-to-validate-a-date) – Andrew Morton Sep 28 '16 at 19:22
  • I think you can't do it: at a first glance this complex regex needs year to validate day (otherwise how can it know if february has 28 or 29 days?) – Zac Sep 28 '16 at 19:22
  • @AndrewMorton: This links to a `JS` question while OP is using `Python`. – Jan Sep 28 '16 at 19:24
  • @Jan Thanks! No-one else told me it was Python. Now I can't VTC with [In python, how to check if a date is valid?](http://stackoverflow.com/a/9988288/1115360) – Andrew Morton Sep 28 '16 at 19:26
  • In view of your edit, in what language is the regex used? – Andrew Morton Sep 28 '16 at 19:47
  • @AndrewMorton the backend uses Python for validation: `re.match(self.validation_regex, string)` – kurtgn Sep 28 '16 at 19:58
  • @kurtgn If you can't change the backend code, please state so in your question. Instead of removing `[.\/\-]`, how about changing it to `[.]` (or `\.`, which is the same) so that you have only removed the slash-or-dash that you don't want? – Andrew Morton Sep 28 '16 at 20:07
  • The ending `$` mark the end of the input string, so it won't match if there is anything remaining. That is why it fails when you remove the dot. Remove `$` also and try again. – zvone Sep 28 '16 at 20:29

2 Answers2

0

Solution

 ^(0[1-9]|[12][0-9]|30(?!\.02)|31(?!\.(0[2469]|11)))\.(0[1-9]|1[0-2])$

Example in python

>>> daymonth_match = r"^(0[1-9]|[12][0-9]|30(?!\.02)|31(?!\.(0[2469]|11)))\.(0[1-9]|1[0-2])$"
>>> print re.match(daymonth_match, "12.04")
<_sre.SRE_Match object at 0x7f3728125880>
>>> print re.match(daymonth_match, "29.02")
<_sre.SRE_Match object at 0x7f3728125880>
>>> print re.match(daymonth_match, "30.02")
None
>>> print re.match(daymonth_match, "30.04")
<_sre.SRE_Match object at 0x7f3728125880>
>>> print re.match(daymonth_match, "31.04")
None
>>> print re.match(daymonth_match, "31.05")
<_sre.SRE_Match object at 0x7f3728125880>

It assumes 29.02 always valid.

Some details on how it works

This regexp relies on the negative lookahead assertion (?!...). For example the expression 30(?!\.02) means that 30 is a match only if it is not followed by \.02 AND, since it is a "look ahead", \.02 is not considered as a match of the expression itself (see python documentation for details)

Zac
  • 2,180
  • 2
  • 23
  • 36
0

Just make the dot and year optional:

(?:[.\/\-]([1-9][0-9]{3}))?
Toto
  • 89,455
  • 62
  • 89
  • 125