使用Python

时间:2018-05-09 09:00:12

标签: python date-parsing

我正在寻找一种方法来解析未知格式的日期,使用以下“元格式”按优先顺序排列:

  1. 日 - 月 - 年(DMY)
  2. 年 - 月 - 日(YMD)
  3. 可能还有其他格式(但这并不重要)
  4. 这是来自挪威,丹麦,芬兰和荷兰的几乎所有发票上的实际元格式,因此它应该是一个常见的用例。但是,似乎没有库可以处理它而无需定义大量可能的格式。

    具体而言。我需要一个方法(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的情况。

5 个答案:

答案 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)
>>>