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
andgroupdict
are used, and the day is obtained from the match withint(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)
```