月/年字符串的 dateutil 解析器

问题描述

与这篇文章有些相关:dateutil parser for month/year format: return beginning of month

给定格式为“Sep-2020”的日期字符串,dateutil.parser.parse 可以正确识别月份和年份,但同时添加了日期。如果提供了认值,则需要一天的时间。否则,它将只使用今天的一天。无论如何要判断解析器是否使用了任何认术语?

例如,如何从下面的三个选项中判断第一种情况下的输入日期字符串不包含日期,而是使用了认值?

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

解决方法

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

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

我编写了自己的类并将其作为 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 方法现在返回这个 SentinelDateTime 类:


>>> 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,0)


>>> d = datetime(1978,1,1)
>>> sentinel = SentinelDateTime(default=d)
>>> s = parser.parse('Sep-2020',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