我正在寻找一种方法来解析未知格式的日期,使用以下“元格式”按优先顺序排列:
这是来自挪威,丹麦,芬兰和荷兰的几乎所有发票上的实际元格式,因此它应该是一个常见的用例。但是,似乎没有库可以处理它而无需定义大量可能的格式。
具体而言。我需要一个方法(parse
)来满足以下要求:
parse("01-02-03") == "datetime.datetime(2003, 2, 1, 0, 0)"
parse("2003-02-01") == "datetime.datetime(2003, 2, 1, 0, 0)"
但它也适用于其他分隔符等。
关于如何在不定义大量格式的情况下实现这一目标的任何建议?
编辑:由于瑞典有不同的偏好,我更喜欢一个可以推广的答案,适用于YMD优于DMY的情况。
答案 0 :(得分:2)
您是否尝试过使用pandas
? Imho这是导入日期的最佳和最干净的方式,因为它在99%的时间内开箱即用,而大多数其他东西如dateutil往往会失败。
import pandas as pd
pd.to_datetime('01-02-03', dayfirst=True)
pd.to_datetime('2003-02-01', dayfirst=True)
pandas的另一个优点是它可以使用数组,列表和大多数其他类型,甚至支持使用datetime-index对数组(称为DataFrames)进行字符串索引。
有关如何使用pandas获取datetime.datetime
格式的更多信息:
只需将.to_pydatetime()
附加到解析器即可。
pd.to_datetime('2003-02-01', dayfirst=True).to_pydatetime()
# Out[]: datetime.datetime(2003, 2, 1, 0, 0)
答案 1 :(得分:1)
在python中查看arrow
库。您可以以任何您喜欢的格式指定日期的格式。例如:
arrow.get("01-02-03","DD-MM-YY")
# gives <Arrow [2003-02-01T00:00:00+00:00]>
arrow.get("01-02-03","YY-MM-DD")
# gives <Arrow [2001-02-03T00:00:00+00:00]>
答案 2 :(得分:0)
看看dateutil.parser.parse
?
from dateutil.parser import parse
parse('01-02-03', dayfirst=True) # datetime.datetime(2003, 2, 1, 0, 0)
parse('2003-02-01') # datetime.datetime(2003, 2, 1, 0, 0)
当然你需要微调parse()
的参数,因为它不会总是猜测它是YDM还是YMD格式,但它是一个好的开始。 Have a look at the documentation for more examples
答案 3 :(得分:0)
正如Scotty1正确指出的那样,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)
通过将dmy + ymd
替换为ymd + dmy
,可以支持“YYD优先于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)
答案 4 :(得分:0)
我试过pandas
当我看到它下载(11.6MB)时我很困惑,当它开始下载numpy
(12.1MB)时我感到很惊讶。
但作为欧洲人,我不需要dateutil
的默认“月优先”行为,所以我现在正在使用它:
import re
sloppy_iso8601 = re.compile('^[12][0-9][0-9][0-9]-[0-9][0-9]?-[0-9][0-9]?.*$')
import dateutil.parser
def parse_date(value, dayfirst=True, yearfirst=False, **kwargs):
if sloppy_iso8601.match(value) is not None:
dayfirst = False
yearfirst = True
return dateutil.parser.parse(value, dayfirst=dayfirst, yearfirst=yearfirst, **kwargs)
表现为OP(和我自己)所期望的。
>>> parse = parse_date
>>> parse("01-02-03")
datetime.datetime(2003, 2, 1, 0, 0)
>>> parse("2003-02-01")
datetime.datetime(2003, 2, 1, 0, 0)
>>>