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.
Related
I am trying to get Python, especially how it works. I was digging into datetime module, and classes, that are part of it - date, datetime, timedelta etc. There are few question, that I don't catch. Lets base on below example:
import datetime
object1 = datetime.date.today()
object2 = datetime.date(2020, 8, 19)
print(object1, object2)
2022-02-12 2020-08-19
print (type(object1), type(object2))
<class 'datetime.date'> <class 'datetime.date'>
print (object1 - object2)
542 days, 0:00:00
The first question is - how it is possible to create object in two ways - first is (as I assume) by using __ init __ constructor (object2), and the second is by ... - there comes a question. How calling method today() directly from class datetime.date is able to create new instance of this class?
My second question is - before, when I was dealing with classes, there wasnt any exact value of object, that I had to use, I was only looking at methods and variables inside this object. The return value of instructions (constructors?) date.today and date(y, m, d) is datetime.date class. But what is the exact type of value, that object1 and object2 contains - how I am able to add them, generally to make any mathematical operation on it with sense.
>>> 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)
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
I'm trying to perform a, to me, simple task of generating a current date/time combo at a speficic time zone. All I see is suggestions to use pytz even though datetime includes the tzinfo class to deal with timezones. However, if I try to use tzinfo, it does not work:
>>> from datetime import datetime, tzinfo
>>> d = datetime.now(tzinfo.tzname("EDT"))
TypeError: descriptor 'tzname' requires a 'datetime.tzinfo' object but received a 'str'
The docs say you can use a time zone name like "EDT" or "GMT". What's wrong with this?
The function tzinfo.tzname does the opposite of what you think it does.
It takes a datetime object and returns a string indicating the time zone.
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.