Skip to main content
3 of 3
added 9 characters in body

I suggest some minor improvements in the regular expression:

  • make sure that the same separator is used between day and month and between month and year with a backreference (?P=sep),
  • replace numbered capture groups with named, and make non-needed groups, if there are any, non-capturing with ?:. Consequently, finditer and groupdict are used, and the day is obtained from the match with int(date['day']), etc. This will make the code somewhat more human-readable.

More importantly, I suggest that you get rid of days, months and years lists altogether. These data can be stored in dictionaries in dates list and filtered prior to appending to dates.

As a consequence, you won't need a loop over range(len(days)).

The validation conditions can be OR'ed together without losing clarity, and I propose to make it a separate function date_is_valid(day: int, month: int, year: int) -> bool.

Also, the only parametre in date_detector can be made typed: def date_detector(text: str):.

To sum suggested modifications up:

import re

def date_is_valid(day: int, month: int, year: int) -> bool:
    return (month not in (2, 4, 6, 9, 11)   # 31 days in month (Jan, Mar, May, Jul, Aug, Oct, Dec).
    or day < 31 and month in (4, 6, 9, 11)  # 30 days in month (Feb, Apr, Jun, Sep, Nov).
    or month == 2 and day == 29 and year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
                                            # February, 29th in a Gregorian leap year.
    or month == 2 and day < 29)             # February, 1st-28th.

def date_detector(text: str):
    date_pattern = re.compile('''
    (?P<day>[12][0-9]|3[0-1]|0?[1-9])   # to detect days from 1 to 31
    (?P<sep>[./-])                      # to detect different separations
    (?P<month>1[0-2]|0?[1-9])           # to detect number of months
    (?P=sep)                            # to detect different seperations
    (?P<year>2?1?[0-9][0-9][0-9])       # to detect number of years from 1000-2999 years
     ''', re.VERBOSE)

    dates = []
    for match in date_pattern.finditer(text):
        date = match.groupdict()                            # convert Match object to dictionary.
        del date['sep']                                     # we don't need the separator any more.
        date = {key: int(val) for key, val in date.items()} # apply int() to all items.
        
        if date_is_valid(date['day'], date['month'], date['year']):
            dates.append(date)

    if len(dates) > 0:
        for date in dates:
            print(date)

data = '30-06-2012, 31-12-2012, 15-02-2002, 29-02-2004, 29-02-2002, 31-02-2004, 31-06-2012'

date_detector(data)
```