Skip to main content
added 9 characters in body
Source Link
  • 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 werare 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.
  • 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 wer 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.
  • 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.
date_is_valid(day: int, month: int, year: int) -> bool
Source Link

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

import re

def date_detectordate_is_valid(text)day:
  int, month: int, date_patternyear: =int) re.compile('''-> bool:
    return (?P<day>[12][0-9]|3[0-1]|0?[1-9])month not in #(2, to4, detect6, days9, from11) 1 to # 31
  days in month (?P<sep>[./-])Jan, Mar, May, Jul, Aug, Oct, Dec).
    or day < 31 and month in (4, 6, 9, 11)  # to30 detectdays differentin separations
month (Feb, Apr, Jun, Sep, (?P<month>1[0-2]|0?[1-9]Nov) .
    or month == 2 and day #== to29 detectand numberyear of% months
4 == 0 and (?P=sep) year % 100 != 0 or year % 400 == 0)
                 # to detect different seperations
    (?P<year>2?1?[0-9][0-9][0-9])       # to detect number of years from 1000-2999 years
    # '''February, re.VERBOSE)

 29th in a datesGregorian =leap []year.
    for match inor date_pattern.finditer(text):
month == 2 and day < 29)  date = match.groupdict()         # 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 convertdays Matchfrom object1 to dictionary.31
     (?P<sep>[./-])   del date['sep']                  # to detect different separations
    (?P<month>1[0-2]|0?[1-9])           # we don't needto thedetect separatornumber anyof more.months
        date = {key: int(val) for key, val in date.items()} # apply int(?P=sep) to all items.
        
         date_is_valid = (
       # to detect different seperations
 date['month'] not in (2, 4, 6, 9, 11?P<year>2?1?[0-9][0-9][0-9])        # to detect #number 31of daysyears infrom month1000-2999 (Jan,years
 Mar, May, Jul, Aug, Oct''', Dec)re.VERBOSE)
       
    dates = or[]
 date['day'] < 31 andfor date['month']match in date_pattern.finditer(4, 6, 9, 11text):
  # 30 days in month (Feb, Apr, Jun,date Sep,= Nov)match.
    groupdict()        or date['month'] == 2 and date['day'] == 29 and date['year'] % 4 == 0 and (date['year'] % 100 != 0 or# date['year']convert %Match 400object ==to 0)dictionary.
                      del date['sep']                                     # February,we 29thdon't inneed athe Gregorianseparator leapany yearmore.
            or date['month'] == 2 and date['day'] < 29  date = {key: int(val) #for Februarykey, 1st-28th.
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)
```

The validation conditions can be OR'ed together without losing clarity, but I propose to make it a boolean variable to improve readability.

import re

def date_detector(text):
     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.
        
         date_is_valid = (
            date['month'] not in (2, 4, 6, 9, 11)           # 31 days in month (Jan, Mar, May, Jul, Aug, Oct, Dec).
            or date['day'] < 31 and date['month'] in (4, 6, 9, 11)  # 30 days in month (Feb, Apr, Jun, Sep, Nov).
            or date['month'] == 2 and date['day'] == 29 and date['year'] % 4 == 0 and (date['year'] % 100 != 0 or date['year'] % 400 == 0)
                                                            # February, 29th in a Gregorian leap year.
            or date['month'] == 2 and date['day'] < 29      # February, 1st-28th.
        )
        
        if date_is_valid:
            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)
```

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.

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)
```
Source Link

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 wer 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.

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, but I propose to make it a boolean variable to improve readability.

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_detector(text):
    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.
        
        date_is_valid = (
            date['month'] not in (2, 4, 6, 9, 11)           # 31 days in month (Jan, Mar, May, Jul, Aug, Oct, Dec).
            or date['day'] < 31 and date['month'] in (4, 6, 9, 11)  # 30 days in month (Feb, Apr, Jun, Sep, Nov).
            or date['month'] == 2 and date['day'] == 29 and date['year'] % 4 == 0 and (date['year'] % 100 != 0 or date['year'] % 400 == 0)
                                                            # February, 29th in a Gregorian leap year.
            or date['month'] == 2 and date['day'] < 29      # February, 1st-28th.
        )
        
        if date_is_valid:
            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)
```