Datetime "fromtimestamp()" ignores inheritance if timezone is not None - python

I created a class inheriting from datetime.datetime.
While creating a new instance using the classmethod fromtimestamp it seems to work, except if I provide a timezone object. In such case, the returned object is of base type datetime.
import time
from datetime import datetime, timezone
class MyDatetime(datetime):
pass
MyDatetime.fromtimestamp(time.time())
# MyDatetime(2018, 11, 30, 18, 8, 36, 418105)
# <class '__main__.MyDatetime'>
MyDatetime.fromtimestamp(time.time(), tz=timezone.utc)
# datetime.datetime(2018, 11, 30, 17, 8, 50, 667385, tzinfo=datetime.timezone.utc)
# <class 'datetime.datetime'>
This seems surprising. I don't see any mention of such weird behavior in the documentation, am I missing something or is it a Python bug?

Cross Posting from Python Bug Tracker
It's a side effect of a date arithmetic operation performed while setting timezone.
while passing tz info to datetime it invokes tz.fromutc()
followed by a datetime arithmetic here
which eventually leads to building a new datetime object here
I'm not sure if its an expected behaviour

Related

How to make an imported variable transparently call a function

Imagine this situation :
I have a file named settings.py that contains
from datetime import datetime
FOO = datetime.now()
Now, in another file, I do from django.conf import settings (the fact that I'm using Django in this specific situation is irrelevant)
In the file I did the import when I use settings.FOO, no matter how much time has passed I will always get the same datetime value, which corresponds to the time when the import happened.
I understand how and why it works that way. My question is : Is there a way to always have an updated value "stored" in settings.FOO without changing the way I access that value ? (as settings.FOO is used in multiple file across multiple subprojects, I aim to keep that syntax)
In other words, I there a way to make my imported variable transparently call a function ?
As mentioned in the comments, there's almost never a good reason for doing it like this, and you would almost always rely on explicit callables for doing what you do, but by explicitly manipulating sys.modules, if all you care about is syntax, it is possible to do what you're trying to do.
settings.py:
from datetime import datetime
import sys
class _Bar:
#property
def FOO(self):
return datetime.now()
sys.modules[__name__] = _Bar()
Usage example:
>>> import settings
>>> settings.FOO
datetime.datetime(2020, 12, 4, 17, 0, 5, 987423)
>>> settings.FOO
datetime.datetime(2020, 12, 4, 17, 0, 7, 285826)

Access timezone informations of datetime object

I can create a timezone specific datetime object like this
import datetime
d = datetime.datetime.now().astimezone()
Result is
datetime.datetime(2018, 4, 2, 15, 12, 2, 807451, tzinfo=datetime.timezone(datetime.timedelta(0, 7200), 'CEST'))
It looks like that tzinfo is represented by two values/attributes: A timedelta and a string. But how can I access them?
I would like to do something like this
d.tzinfo.delta
d.tzinfo.name
I need this informations to be able to (de)serialize the datetime to and from JSON.
I don't want to use third-party packages for such solutions.
tzinfo in this case is an instance of the datetime.timezone() class:
The timezone class is a subclass of tzinfo, each instance of which represents a timezone defined by a fixed offset from UTC.
You can use the tzinfo.utcoffset() and tzinfo.utcname() methods to access the delta and name. For timezone() instances the argument each of these take is ignored, but normally you'd pass in the datetime instance they are attached to:
d.tzinfo.utcoffset(d)
d.tzinfo.utcname(d)
You'd usually call these on the datetime.datetime instance, which has the same methods (but which take no arguments) and these will then handle passing in the right argument to the methods on the contained tzinfo attribute.
Demo:
>>> import datetime
>>> d = datetime.datetime.now().astimezone()
>>> d.utcoffset()
datetime.timedelta(seconds=7200)
>>> d.tzname()
'CEST'
>>> d.tzinfo.utcoffset(d)
datetime.timedelta(seconds=7200)
>>> d.tzinfo.utcoffset(d) is d.utcoffset() # they are the same object
True
The datetime.timezone() subclass is just one implementation of a tzinfo time zone, 3rd-party libraries like pytz offer their own, and the utcoffset() and tzname() return values may well vary for timezones with historical information attached.
You're asking about datetime.tzinfo, which is an abstract base class, as documented here.
datetime comes with an implementation of the tzinfo abstract base class called datetime.timezone, which is documented here.
Just below that last link are the docs on timezone.utcoffset and timezone.utcname, which are ways to access the properties you asked about on the tzinfo if it is a timezone. However, this is not the only implementation of that abstract class. If you are using the pytz timezone, for example, then you'll need to read the docs on that instead.

Is this the right way to set a timezone with dateutil?

>>> import dateutil.parser, dateutil.tz as tz
>>> dateutil.parser.parse('2017-08-09 10:45 am').replace(tzinfo=tz.gettz('America/New_York'))
datetime.datetime(2017, 8, 9, 10, 45, tzinfo=tzfile('/usr/share/zoneinfo/America/New_York'))
Is that really the way that we're supposed to set a default timezone for parsing? I've read the documentation for the parser and examples but I cannot seem to find anything that says, "This is how to set the default timezone for dateutil.parser.parse", or even anything like it.
Because while this works, there are cases where it would do the wrong thing, if the zone were provided. Does that mean we should do this?
>>> d = dateutil.parser.parse('2017-08-09 10:45 am +06:00')
>>> d = d.replace(tzinfo=d.tzinfo or tz.gettz('America/Chicago'))
Because that's clunky, too.
What's the recommended way to set a default timezone when parsing?
There are basically two "correct" ways to do this. You can see that this was brought up as Issue #94 on dateutil's issue tracker, and "set a default time zone" is determined to be out of scope, since this is something that can be easily done with the information returned by the parser anyway (and thus no need to build it in to the parser itself). The two ways are:
Provide a default date that has a time zone. If you don't care what the default date is, you can just specify some date literal and be done with it. If you want the behavior to be basically the same as dateutil's default behavior (replacing missing elements from "today's date at midnight"), you have to have a bit of boilerplate:
from datetime import datetime, time
from dateutil import tz, parser
default_date = datetime.combine(datetime.now(),
time(0, tzinfo=tz.gettz("America/New_York")))
dt = parser.parse(some_dt_str, default=default_date)
Use your second method with .replace:
from dateutil import parser
def my_parser(*args, default_tzinfo=tz.gettz("America/New_York"), **kwargs):
dt = parser.parse(*args, **kwargs)
return dt.replace(tzinfo=dt.tzinfo or default_tzinfo)
This last one is probably slightly cleaner than the first, but has a slight performance decrease if run in a tight loop (since the first one only needs the default date created once), but dateutil's parser is actually quite slow, so an extra date construction is likely the least of your problems if you're running it in a tight loop.
Fleshing out Paul's comment - because a datetime has to be at least a year, month, and day, dateutil already has a default that it uses:
>>> from datetime import datetime
>>> datetime.now()
datetime.datetime(2017, 10, 13, 15, 16, 13, 548750)
>>> dateutil.parser.parse('2017')
datetime.datetime(2017, 10, 13, 0, 0)
Given this, the appropriate choice would be to create a default that contains the timezone and is either just the current date, or whatever date makes sense:
>>> dateutil.parser.parse('2017', default=datetime(2017, 10, 13, tzinfo=tz.gettz('America/New_York')))
Naturally you can store the default as something sensible, like default_datetime or something, then it becomes:
>>> dateutil.parser.parse('2017', default=default_datetime)

How do I construct a UTC `datetime` object in Python?

I am using the datetime.datetime class from the Python standard library. I wish to construct an instance of this class with the UTC timezone. To do so, I gather that I need to pass as the tzinfo argument to the datetime constructor some instance of the tzinfo class.
The documentation for the tzinfo class says that:
tzinfo is an abstract base class, meaning that this class should not be instantiated directly. You need to derive a concrete subclass, and (at least) supply implementations of the standard tzinfo methods needed by the datetime methods you use. The datetime module does not supply any concrete subclasses of tzinfo.
Now I'm stumped. All I want to do is represent "UTC". I should be able to do that using approximately three characters, like this
import timezones
...
t = datetime(2015, 2, 1, 15, 16, 17, 345, timezones.UTC)
In short, I'm not going to do what the documentation tells me to do. So what's my alternative?
There are fixed-offset timezones in the stdlib since Python 3.2:
from datetime import datetime, timezone
t = datetime(2015, 2, 1, 15, 16, 17, 345, tzinfo=timezone.utc)
Constructor is :
datetime(year, month, day, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0)
Docs link.
Though it is easy to implement utc timezone on earlier versions:
from datetime import tzinfo, timedelta, datetime
ZERO = timedelta(0)
class UTCtzinfo(tzinfo):
def utcoffset(self, dt):
return ZERO
def tzname(self, dt):
return "UTC"
def dst(self, dt):
return ZERO
utc = UTCtzinfo()
t = datetime(2015, 2, 1, 15, 16, 17, 345, tzinfo=utc)
I used a lot in pytz and very satisfied from this module.
pytz
pytz brings the Olson tz database into Python. This library allows
accurate and cross platform timezone calculations using Python 2.4
or higher. It also solves the issue of ambiguous times at the end
of daylight saving time, which you can read more about in the Python
Library Reference (datetime.tzinfo).
Also I would recommend for reading: Understanding DateTime, tzinfo, timedelta & TimeZone Conversions in python

Obtain an aware datetime object out of a string

Is there a way I can obtain a datetime aware object out of a string in Python using only the standard library modules ?
I know that I can use dateutil.parser.parse, but unfortunately that's not a good enough reason to add it as a dependency to my project. I already have the mx.DateTime module as a dependency, buuttttt:
>>> dateutil.parser.parse('2011-10-24T06:51:47-07:00')
datetime.datetime(2011, 10, 24, 6, 51, 47, tzinfo=tzoffset(None, -25200))
>>> mx.DateTime.ISO.ParseDateTimeUTC('2011-10-24T06:51:47-07:00')
<mx.DateTime.DateTime object for '2011-10-24 13:51:47.00' at 29c7e48>
the ParseDateTimeUTC fails to detect the offset, even though in its documentation says that:
Returns a DateTime instance in UTC reflecting the given ISO
date. A time part is optional and must be delimited from the
date by a space or 'T'. Timezones are honored.
mx.DateTime.ISO.ParseDateTimeUTC is doing the right thing - it is applying the specified timezone to adjust the time to UTC. The resulting UTC time doesn't have a timezone because it isn't a local time anymore.
The standard Python library doesn't contain any concrete timezone classes according to the documentation:
tzinfo is an abstract base clase, meaning that this class should not
be instantiated directly. You need to derive a concrete subclass, and
(at least) supply implementations of the standard tzinfo methods
needed by the datetime methods you use. The datetime module does not
supply any concrete subclasses of tzinfo.
I've always been surprised that they didn't at least include a class for UTC, or a generic implementation like dateutil's tzoffset.

Categories