Subclassing is almost always preferred over trying to monkey patch. Does the following work?
class LimitedDatetime(datetime.datetime):
format = "%y-%m-%dT%H:%M:%S"
def __new__(cls, date_string):
return super(LimitedDatetime, cls).strptime(date_string, cls.format)
def __str__(self):
return super(LimitedDatetime, self).strftime(self.format)
LD = LimitedDatetime("2017-10-02T07:24:21")
print str(LD)
adapted from What does 'super' do in Python?, which describes specifically how classmethod alternate constructors are invoked using super
.
EDIT:
That didn't seem to work due because the alternate constructor returns the entire object that seems to want to be passed through the strptime
again, but the following works, don't inherit from datetime
, create a new object that is LIKE datetime
in every other way but overriding the appropriate methods (str
callable). This is called composition (and you don't need super
):
import datetime
class LimitedDatetime(object):
fmt = r'%Y-%m-%dT%H:%M:%S'
# fake isinstance checking:
@property
def __class__(self):
return datetime.datetime
def __init__(self, date_string):
self._ = datetime.datetime.strptime(date_string, self.fmt)
# this lets us access all of datetime's attributes as if they were our own
def __getattr__(self, attr):
return getattr(self._, attr)
def __str__(self):
return self.strftime(self.fmt)
LD = LimitedDatetime('2017-10-02T07:24:21')
print type(LD)
print isinstance(LD, datetime.datetime)
print str(LD)
> <class '__main__.LimitedDatetime'>
> True
> 2017-01-01T01:01:01
This approach is from inheritance from str or int, with the class faking from How to fake type with Python
EDIT 2018-02-09
Operators are special methods (__op__
), so they have to be overridden:
def __add__(self, td):
return LimitedDatetime(self._ + td)
# support `timedelta + LimitedDatetime`
def __radd__(self, td):
return LimitedDatetime(self._ + td)
The rule for operator methods is they need to return a new instance of the object with the new state, and this means will need to adjust the signature of the constructor to support calling it 2 different ways (one, the original date string, the second, with an existing datetime
object, which is what an operator with a timedelta
object returns:
def __init__(self, obj):
if isinstance(obj, basestring):
# support LimitedDatetime('YYYY-MM-DD')
self._ = datetime.datetime.strptime(obj, self.fmt)
elif isinstance(obj, datetime.datetime):
# support re-instantiation from operators
self._ = obj
td = datetime.timedelta(1)
enddate = LD + td
print str(enddate)
print type(enddate)
# need __radd__ to support:
enddate = td + LD
print str(enddate)
print type(enddate)
> 2017-01-02T01:01:01
> <class '__main__.LimitedDatetime'>
> 2017-01-02T01:01:01
> <class '__main__.LimitedDatetime'>