Somewhat related to this post: dateutil parser for month/year format: return beginning of month
Given a date string of the form 'Sep-2020', dateutil.parser.parse
correctly identifies the month and the year but adds the day as well. If a default is provided, it takes the day from it. Else, it will just use today's day. Is there anyway to tell if the parser used any of the default terms?
For example, how can I tell from the three options below that the input date string in the first case did not include day and that the default value was used?
>>> 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)
``
I did something a little mad to solve this. It's mad since it's not guaranteed to work with future versions of dateutil
(since it's relying on some dateutil internals).
Currently I'm using: python-dateutil 2.8.1
.
I wrote my own class and passed it as default
to the parser:
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
)
The dateutils method now returns this 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)
I wrote this answer into a little package: https://github.com/foxyblue/sentinel-datetime