Как правильно заметил Скотти1, pandas.to_datetime
на самом деле работает для описанного мной варианта использования, однако он не обобщается на случай использования, где YMD предпочтительнее, чем DMY (что является предпочтением в Швеции) .
В итоге у меня получилось что-то, что работает в более чем 95% моих дел, что намного лучше, чем любая из существующих библиотек анализа даты, которые можно найти прямо из коробки. Вот мое решение:
def parse(string):
dmy = ['%d{sep}%m{sep}%Y', '%d{sep}%m{sep}%y']
ymd = ['%Y{sep}%m{sep}%d', '%y{sep}%m{sep}%d']
seperators = ['', ' ', '-', '.', '/']
formats = [f.format(sep=sep) for f in dmy + ymd for sep in seperators]
additional = ['%d/%m %Y']
return dateparser.parse(string, date_formats=formats + additional)
Поддержка "YMD предпочтительнее DMY" может быть достигнута путем замены dmy + ymd
на ymd + dmy
.
Чтобы помочь сообщить о поведении кода выше, вот набор тестов, которые все проходят:
out = datetime.datetime(2003, 2, 1, 0, 0)
# straight forward DMY
assert out == extractors.extract_date('010203')
assert out == extractors.extract_date('01022003')
assert out == extractors.extract_date('01-02-03')
assert out == extractors.extract_date('01-02-2003')
# alternative delimiters
assert out == extractors.extract_date('01.02.03')
assert out == extractors.extract_date('01 02 03')
assert out == extractors.extract_date('01/02/03')
assert out == extractors.extract_date('01/02 2003')
# YMD (when the first cannot parse as a day, default to YMD)
assert out == extractors.extract_date('2003-02-01')
assert extractors.extract_date('98-02-01') == \
datetime.datetime(1998, 2, 1, 0, 0)
# single digits
assert out == extractors.extract_date('1-2-2003')
assert out == extractors.extract_date('1/2 2003')
assert out == extractors.extract_date('2003-2-1')
# when there are not other possibilities (MDY, YDM)
assert extractors.extract_date('12-31-98') == \
datetime.datetime(1998, 12, 31, 0, 0)
assert extractors.extract_date('98-31-12') == \
datetime.datetime(1998, 12, 31, 0, 0)