使用Python以北欧格式(先是DMY,然后是YMD)解析日期的最佳方法
我正在寻找一种按照优先顺序使用以下“元格式”解析未知格式日期的方法:使用Python以北欧格式(先是DMY,然后是YMD)解析日期的最佳方法,python,date-parsing,Python,Date Parsing,我正在寻找一种按照优先顺序使用以下“元格式”解析未知格式日期的方法: 年月日(DMY) 年-月-日(YMD) 潜在的其他格式(但这并不重要) 这是挪威、丹麦、芬兰和荷兰几乎所有发票上的实际元格式,因此它应该是一个常见的用例。然而,似乎没有任何一个库能够处理它,而不必定义大量可能的格式列表 具体来说。我需要一个方法(parse)来满足以下要求: 解析(“01-02-03”)=“datetime.datetime(2003,2,1,0,0)” 解析(“2003-02-01”)=“datetime.
parse
)来满足以下要求:
解析(“01-02-03”)=“datetime.datetime(2003,2,1,0,0)”
解析(“2003-02-01”)=“datetime.datetime(2003,2,1,0,0)”
但它也适用于其他分隔符等
关于如何在不定义大量格式的情况下实现这一点,有什么建议吗
编辑:由于瑞典有不同的偏好,我更喜欢一个可以推广到YMD优于DMY的情况下的答案。看看
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格式,但这是一个好的开始 你试过使用熊猫吗?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索引对数组(称为DataFrames)进行字符串索引
有关如何使用pandas获取datetime.datetime
格式的更多信息:只需将
.to_pydatetime()
附加到解析器中即可
pd.to_datetime('2003-02-01', dayfirst=True).to_pydatetime()
# Out[]: datetime.datetime(2003, 2, 1, 0, 0)
查看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]>
arrow.get(“01-02-03”,“DD-MM-YY”)
#给予
箭头。get(“01-02-03”,“YY-MM-DD”)
#给予
正如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
,可以实现对“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)
我尝试了
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(和我自己)所期望的一样
如果没有为每种情况指定参数,这并不能在所有情况下都起作用<代码>解析('2003-02-01',dayfirst=True)生成
datetime.datetime(2003,1,2,0,0)
,这是不正确的。在第一种情况下,省略dayfirst=True
会产生一个错误的日期。是的,但是再次-确定用户键入日期时的含义并不容易。例如,考虑<代码> 01 / 02 / 03 < /代码> -你如何知道哪一个是一天、一个月和一年?在美国可能是2003年第1个月第2天,在欧洲可能是2003年第2个月第1天。如果没有额外的提示,猜测是不可能的。他表示,他主要使用挪威、丹麦、芬兰和荷兰使用的格式,通常是dayfirst=True
或ISO date。如果有使用美国日期的特殊情况,在没有指定其他信息的情况下,无法解析它,但这应被视为特殊情况。困难在于,即使使用最简单的情况-可能的日期格式字符串列表,你一个接一个地尝试,仍然有可能得到一个有效的结果。例如,无法猜测哪个月是从01/02/2003
开始的,但可以猜测01/13/2003
开始的月份。如果您正在解析用户输入,您可以通过查看区域设置来帮助自己。如果是一个网站,您可以查看请求的来源国(或查看Accept Language
headers)。对于发票,您可以使用发票元数据-发票颁发者的国家/地区。如果您能够提出使用本地语言的解决方案,这是一个非常有效的解决方案。正如我提到的,我确实知道日期是北欧的。这个问题的作者似乎在寻找一个通用的“魔法”函数,它可以猜测格式,而不需要指定格式。哦,我明白了。也许可以使用已知格式的列表并解析每种格式的日期?是的,或者可以像下面的箭头那样链接它们。get('01-02-03',['DD-MM-YY','YY-MM-DD'])
,但是当尝试扩展到一般情况时,它很快就失控了。仅对于DMY,一个简单的最小值看起来像这样'DDMMYY','DD-MM-YY','DD/MM/YY','DD.MM.YY','DDMMYYYY','DD-MM-YYYY','DD.MM.YYYY'
(当然可以通过编程方式构建)。如果没有通用的日期分析器,这是一个很好的解决方案。谢谢事实证明,arrow
不是按顺序尝试多种格式的合适库,但是像dateparser
这样的库可以很好地实现这一点。下面是一个示例,说明了arrow
失败的原因:arrow.get('2003-02-01',['DD-MM-YY','yyyyy-MM-DD'])
返回
这是以一般方式解决问题。谢谢斯科蒂。但让我告诉你一件事
>>> 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)
>>>