month/year 个字符串的 dateutil 解析器

dateutil parser for month/year strings

与此有点相关 post:

给定 'Sep-2020' 形式的日期字符串,dateutil.parser.parse 正确识别了月份和年份,但也添加了日期。如果提供默认值,则需要一天的时间。否则,它将只使用今天。有没有办法判断解析器是否使用了任何默认术语?

例如,我如何从下面的三个选项中判断出第一种情况中输入的日期字符串不包括日期并且使用了默认值?

>>> from datetime import datetime
>>> from dateutil import parser
>>> d = datetime(1978, 1, 1, 0, 0)
>>> parser.parse('Sep-2020', default=d)
datetime.datetime(2020, 9, 1, 0, 0)
>>> parser.parse('1-Sep-2020', default=d)
datetime.datetime(2020, 9, 1, 0, 0)
>>> parser.parse('Sep-1-2020', default=d)
datetime.datetime(2020, 9, 1, 0, 0)
``

我做了一些疯狂的事情来解决这个问题。这很疯狂,因为它不能保证与 dateutil 的未来版本一起使用(因为它依赖于 some dateutil 内部)。

目前我正在使用:python-dateutil 2.8.1

我写了自己的 class 并将其作为 default 传递给解析器:

from datetime import datetime


class SentinelDateTime:

    def __init__(self, year=0, month=0, day=0, default=None):
        self._year = year
        self._month = month
        self._day = day

        if default is None:
            default = datetime.now().replace(
                hour=0, minute=0,
                second=0, microsecond=0
            )

        self.year = default.year
        self.month = default.month
        self.day = default.day
        self.default = default

    @property
    def has_year(self):
        return self._year != 0

    @property
    def has_month(self):
        return self._month != 0

    @property
    def has_day(self):
        return self._day != 0

    def todatetime(self):
        res = {
            attr: value
            for attr, value in [
                ("year", self._year),
                ("month", self._month),
                ("day", self._day),
            ] if value
        }
        return self.default.replace(**res)

    def replace(self, **result):
        return SentinelDateTime(**result, default=self.default)

    def __repr__(self):
        return "%s(%d, %d, %d)" % (
            self.__class__.__qualname__,
            self._year,
            self._month,
            self._day
        )

dateutils 方法现在 returns 这个 SentinelDateTime class:


>>> from dateutil import parser
>>> from datetime import datetime
>>> from snippet1 import SentinelDateTime
>>>
>>> sentinel = SentinelDateTime()
>>> s = parser.parse('Sep-2020', default=sentinel)
>>> s
SentinelDateTime(2020, 9, 0)
>>> s.has_day
False
>>> s.todatetime()
datetime.datetime(2020, 9, 9, 0, 0)


>>> d = datetime(1978, 1, 1)
>>> sentinel = SentinelDateTime(default=d)
>>> s = parser.parse('Sep-2020', default=sentinel)
>>> s
SentinelDateTime(2020, 9, 0)
>>> s.has_day
False
>>> s.todatetime()
datetime.datetime(2020, 9, 1, 0, 0)

我把这个答案写进了一个小包里:https://github.com/foxyblue/sentinel-datetime

我找到了一个不太复杂的解决方案:

from datetime import datetime
from dataclasses import dataclass

from dateutil import parser


@dataclass
class Result:
    dt: datetime
    data: dict


class subparser(parser.parser):

    def _build_naive(self, res, default):
        naive = super()._build_naive(res, default)
        return Result(dt=naive, data=res)

举个例子:

>>> PARSER = subparser()
>>> info = PARSER.parse("2020")
>>> info.data.year)
2020
>>> info.data.month
None
>>> info.dt
2020-01-10 00:00:00