Why does datetime.datetime.utcnow() not contain timezone information?
Asked Answered
H

10

467
datetime.datetime.utcnow()

Why does this datetime not have any timezone info given that it is explicitly a UTC datetime?

I would expect that this would contain tzinfo.

Herbertherbicide answered 25/2, 2010 at 4:14 Comment(1)
How to convert a normal iso format date field which is of type string to utc format?Hoashis
S
250

That means it is timezone naive, so you can't use it with datetime.astimezone

you can give it a timezone like this

import pytz  # 3rd party: $ pip install pytz

u = datetime.utcnow()
u = u.replace(tzinfo=pytz.utc) #NOTE: it works only with a fixed utc offset

now you can change timezones

print(u.astimezone(pytz.timezone("America/New_York")))

To get the current time in a given timezone, you could pass tzinfo to datetime.now() directly:

#!/usr/bin/env python
from datetime import datetime
import pytz # $ pip install pytz

print(datetime.now(pytz.timezone("America/New_York")))

It works for any timezone including those that observe daylight saving time (DST) i.e., it works for timezones that may have different utc offsets at different times (non-fixed utc offset). Don't use tz.localize(datetime.now()) -- it may fail during end-of-DST transition when the local time is ambiguous.

Selfinductance answered 25/2, 2010 at 4:28 Comment(16)
But there's no good reason for it to be timezone naive - it's specified to be UTC. Why do you need to search out a third party library to make it work properly?Katharynkathe
I think it may have been a licensing thing, so pytz can't be included in the stdlibSelfinductance
I agree; for me ‘naïve’ times are completely useless. There is discussion on the python list at the moment about adding pytz to the stdlib; the issue is not licensing but the fact that the timezone data is updated so often (which Python itself can't be). Also pytz doesn't implement the tzinfo interface in the expected way so you can get errors if you try to use some of the city timezones in astimezone. So datetime not only has no native timezones, but the only widely-available implementation of tzinfo is non-compliant to the supposed standard.Oleander
@Oleander Why don't pytz and the standard datetime libraries work for you? The Python core and pytz evolving as independent projects reduces logistical complexity for the core team. Yes, reducing the complexity for the Python core team increases the complexity for all of the Python users who need to deal with timezones but, I trust they made this decision for a good reason. The rule "The standard library has no tzinfo instances..." is great because it's simple, why make an exception here?Covering
How about just u=datetime.now(pytz.utc)Edgeworth
@Oleander The lack of tzinfo in the standard library is surely a problem, but I don't see how writing your own code as a solution it's easier, faster, or better than using the pytz library which is the de-facto standard in the python world.Urdu
I use datetime/pytz in some projects (eg where interacting with Django), but I find it very inconvenient. You invariably end up with a mixture of naïve times, UTC times, local times, local DST times, and other-server-local-times (eg DB). The result is confusion and bugs. It was ultimately a mistake to try to shoehorn fundamentally different models (an abstract calendar point, an absolute universal moment, and a combination of both) into the same datatype. I much prefer dealing with only a timestamp for data, combined with regional formatting/parsing.Oleander
tzinfo= does not work with pytz for DST timezones, see this answer.Sluggish
@bain: don't use tz.localize(datetime.now()); use datetime.now(tz) instead.Occasionally
@J.F Is that always correct? pytz "This library differs from the documented Python API for tzinfo implementations; if you want to create local wallclock times you need to use the localize() method documented in this document." - the docs do not make clear when it is safe to ignore that rule, perhaps it is always safe for datetime.now (?) but in general passing a pytz timezone to a datetime is risky e.g. datetime(2015, 1, 1, tzinfo=pytz.timezone('Europe/Paris'))Sluggish
@bain: now(tz) is always correct. We are not discussing anything else here.Occasionally
If I have to guess why utcnow() does not include tzinfo, it's probably because of leap seconds. In other words, even UTC is subject to change. If utcnow() included a tzinfo, there are bound to be naïve programmers who don't realize that even the utc tzinfo needs to be updated from time to time, and then they run into time-related bugs and would still scratch their heads.Pipeline
@J.F Author of pytz says now(tz) is not always correct: 'even "datetime.now(pytz.timezone('Australia/Melbourne'))" could give you a timestamp out by one hour if you were unlucky and the DST transition occurred while that statement was being run.'Sluggish
@bain: it just proves that even the pytz author can be wrong. I've seen the code. now(tz) that calls tz.fromutc internally works even during DST transitions (because there is no DST transitions in utc). If you think I'm wrong; provide specific example (date, timezone, python, pytz version)Occasionally
A long time later from the original answer, but a relatively safe localize way built into pytz: pytz.utc.localize(datetime.utcnow()). I've found this also incorporates the DST variations without issue.Dyan
In 3.9+ python added the ZoneInfo class which can be used as ZoneInfo('America/Denver') and not use a 3rd party lib. Note that if the operating system doesn't have the zone files available it will fall back to tzdata from pypi (you'd need to install it). See PEP615Makedamakefast
E
358

Note that for Python 3.2 onwards, the datetime module contains datetime.timezone. The documentation for datetime.utcnow() says:

An aware current UTC datetime can be obtained by calling datetime.now(timezone.utc).

So, datetime.utcnow() doesn't set tzinfo to indicate that it is UTC, but datetime.now(datetime.timezone.utc) does return UTC time with tzinfo set.

So you can do:

>>> import datetime
>>> datetime.datetime.now(datetime.timezone.utc)
datetime.datetime(2014, 7, 10, 2, 43, 55, 230107, tzinfo=datetime.timezone.utc)

Since Python 3.11, there also exists datetime.UTC which is equivalent to datetime.timezone.utc. So you can also do datetime.datetime.now(datetime.UTC).

Edgeworth answered 10/7, 2014 at 2:43 Comment(11)
Which is prefered? datetime.now(timezone.utc) or datetime.utcnow(timezone.utc)?Commute
datetime.utcnow() takes no arguments. So it would have to be datetime.now(timezone.utc).Edgeworth
datetime.now() will return the machine time but datetime.utcnow() will return the actual UTC time.Clorindaclorinde
@Babu: datetime.utcnow() doesn't set tzinfo to indicate that it is UTC. But datetime.now(datetime.timezone.utc) does return UTC time with tzinfo set.Edgeworth
@CraigMcQueen So if we pass a tz object in the now constructor it will return time of that timezone? Ok! Thanks for pointing out.Clorindaclorinde
I think this is the proper solution now, one shouldn't need to add an external dependency to specify the timezone metadata when it's already explicitly UTCEffloresce
Never use datetime.utcnow(). It doesn't set tzinfo though it is utc, but naive datetime object is presumed to represent system local time by datetime library. If your system local time is not UTC, datetime.datetime.utcnow().astimezone(datetime.timezone.utc) gives wrong result.Dube
This answer should be the accepted oneAcculturate
Didn't answer the question - why does it not contain timezone information?Hinder
@BB. the question is unanswerable as asked. But this answers the question behind the question - how do you get a datetime object in UTC with the proper tzinfo attached?Katharynkathe
Heads up, as of Python 3.12 datetime.utcnow() is deprecated in favor of datetime.now(timezone.utc)Shortcircuit
S
250

That means it is timezone naive, so you can't use it with datetime.astimezone

you can give it a timezone like this

import pytz  # 3rd party: $ pip install pytz

u = datetime.utcnow()
u = u.replace(tzinfo=pytz.utc) #NOTE: it works only with a fixed utc offset

now you can change timezones

print(u.astimezone(pytz.timezone("America/New_York")))

To get the current time in a given timezone, you could pass tzinfo to datetime.now() directly:

#!/usr/bin/env python
from datetime import datetime
import pytz # $ pip install pytz

print(datetime.now(pytz.timezone("America/New_York")))

It works for any timezone including those that observe daylight saving time (DST) i.e., it works for timezones that may have different utc offsets at different times (non-fixed utc offset). Don't use tz.localize(datetime.now()) -- it may fail during end-of-DST transition when the local time is ambiguous.

Selfinductance answered 25/2, 2010 at 4:28 Comment(16)
But there's no good reason for it to be timezone naive - it's specified to be UTC. Why do you need to search out a third party library to make it work properly?Katharynkathe
I think it may have been a licensing thing, so pytz can't be included in the stdlibSelfinductance
I agree; for me ‘naïve’ times are completely useless. There is discussion on the python list at the moment about adding pytz to the stdlib; the issue is not licensing but the fact that the timezone data is updated so often (which Python itself can't be). Also pytz doesn't implement the tzinfo interface in the expected way so you can get errors if you try to use some of the city timezones in astimezone. So datetime not only has no native timezones, but the only widely-available implementation of tzinfo is non-compliant to the supposed standard.Oleander
@Oleander Why don't pytz and the standard datetime libraries work for you? The Python core and pytz evolving as independent projects reduces logistical complexity for the core team. Yes, reducing the complexity for the Python core team increases the complexity for all of the Python users who need to deal with timezones but, I trust they made this decision for a good reason. The rule "The standard library has no tzinfo instances..." is great because it's simple, why make an exception here?Covering
How about just u=datetime.now(pytz.utc)Edgeworth
@Oleander The lack of tzinfo in the standard library is surely a problem, but I don't see how writing your own code as a solution it's easier, faster, or better than using the pytz library which is the de-facto standard in the python world.Urdu
I use datetime/pytz in some projects (eg where interacting with Django), but I find it very inconvenient. You invariably end up with a mixture of naïve times, UTC times, local times, local DST times, and other-server-local-times (eg DB). The result is confusion and bugs. It was ultimately a mistake to try to shoehorn fundamentally different models (an abstract calendar point, an absolute universal moment, and a combination of both) into the same datatype. I much prefer dealing with only a timestamp for data, combined with regional formatting/parsing.Oleander
tzinfo= does not work with pytz for DST timezones, see this answer.Sluggish
@bain: don't use tz.localize(datetime.now()); use datetime.now(tz) instead.Occasionally
@J.F Is that always correct? pytz "This library differs from the documented Python API for tzinfo implementations; if you want to create local wallclock times you need to use the localize() method documented in this document." - the docs do not make clear when it is safe to ignore that rule, perhaps it is always safe for datetime.now (?) but in general passing a pytz timezone to a datetime is risky e.g. datetime(2015, 1, 1, tzinfo=pytz.timezone('Europe/Paris'))Sluggish
@bain: now(tz) is always correct. We are not discussing anything else here.Occasionally
If I have to guess why utcnow() does not include tzinfo, it's probably because of leap seconds. In other words, even UTC is subject to change. If utcnow() included a tzinfo, there are bound to be naïve programmers who don't realize that even the utc tzinfo needs to be updated from time to time, and then they run into time-related bugs and would still scratch their heads.Pipeline
@J.F Author of pytz says now(tz) is not always correct: 'even "datetime.now(pytz.timezone('Australia/Melbourne'))" could give you a timestamp out by one hour if you were unlucky and the DST transition occurred while that statement was being run.'Sluggish
@bain: it just proves that even the pytz author can be wrong. I've seen the code. now(tz) that calls tz.fromutc internally works even during DST transitions (because there is no DST transitions in utc). If you think I'm wrong; provide specific example (date, timezone, python, pytz version)Occasionally
A long time later from the original answer, but a relatively safe localize way built into pytz: pytz.utc.localize(datetime.utcnow()). I've found this also incorporates the DST variations without issue.Dyan
In 3.9+ python added the ZoneInfo class which can be used as ZoneInfo('America/Denver') and not use a 3rd party lib. Note that if the operating system doesn't have the zone files available it will fall back to tzdata from pypi (you'd need to install it). See PEP615Makedamakefast
K
88

The standard Python libraries didn't include any tzinfo classes until Python 3.2. I can only guess at the reasons. Personally I think it was a mistake not to include a tzinfo class for UTC, because that one is uncontroversial enough to have a standard implementation. Although there was no implementation in the library, there is one given as an example in the tzinfo documentation.

from datetime import timedelta, tzinfo

ZERO = timedelta(0)

# A UTC class.

class UTC(tzinfo):
    """UTC"""

    def utcoffset(self, dt):
        return ZERO

    def tzname(self, dt):
        return "UTC"

    def dst(self, dt):
        return ZERO

utc = UTC()

Once you have a UTC tzinfo object, you still can't use it with utcnow. To get the current time as an aware datetime object:

from datetime import datetime 

now = datetime.now(utc)

In Python 3.2 they finally put a UTC tzinfo class in the library:

from datetime import datetime, timezone 

now = datetime.now(timezone.utc)

In Python 3.9 they created tzinfo classes for all the other time zones. See PEP 615 -- Support for the IANA Time Zone Database in the Standard Library for all the details.

Katharynkathe answered 25/2, 2010 at 4:27 Comment(6)
Go figure as to why this class was not provided in the first place (and, more importantly, used for datetime objects created by utcnow())...Hold
@rgove, that's the kind of righting of wrongs that was supposed to be fair game for Python 3. They shouldn't have worried about the backward compatibility. There's another example I read within the last few days - the struct module would do automatic conversions from Unicode to bytestring, and the final decision was to break compatibility with earlier Python 3 versions to prevent a bad decision from going forward.Katharynkathe
I'm dumbfounded that Python's tzinfo documentation includes examples of code to implement it, but they don't include that functionality in datetime itself! docs.python.org/2/library/datetime.html#datetime.tzinfo.fromutcChuch
Also, the example UTC class shown here is almost exactly what the pytz module's implementation contains. It adds a few helpful methods.Chuch
@LS yes, pytz is a great resource. By the time I had edited my answer to put in the example code, somebody else had already suggested it and I didn't want to steal their thunder.Katharynkathe
This is the right answer. For backward compatibility, utcnow() is kept as it is.Elatia
B
21

The pytz module is one option, and there is another python-dateutil, which although is also third party package, may already be available depending on your other dependencies and operating system.

I just wanted to include this methodology for reference- if you've already installed python-dateutil for other purposes, you can use its tzinfo instead of duplicating with pytz

import datetime
import dateutil.tz

# Get the UTC time with datetime.now:
utcdt = datetime.datetime.now(dateutil.tz.tzutc())

# Get the UTC time with datetime.utcnow:
utcdt = datetime.datetime.utcnow()
utcdt = utcdt.replace(tzinfo=dateutil.tz.tzutc())

# For fun- get the local time
localdt = datetime.datetime.now(dateutil.tz.tzlocal())

I tend to agree that calls to utcnow should include the UTC timezone information. I suspect that this is not included because the native datetime library defaults to naive datetimes for cross compatibility.

Binns answered 7/11, 2013 at 15:3 Comment(6)
NameError: name 'dt' is not definedNocturne
I was using the datetime.datetime.utcfromtimestamp() call, and needing to add tzinfo, The second solution worked for me: utcdt = datetime.datetime.utcfromtimestamp(1234567890).replace(dateutil.tz.tzutc())Impulsion
note: unlike datetime.now(pytz_tz) that always works; datetime.now(dateutil.tz.tzlocal()) may fail during DST transitions. PEP 495 -- Local Time Disambiguation might improve dateutil situation in the future.Occasionally
@IanLee: you could use utc_dt = datetime.fromtimestamp(1234567890, dateutil.tz.tzutc()) (note: dateutil with a non-fixed utc offset (such as dateutil.tz.tzlocal()) may fail here, use a pytz-based solution instead).Occasionally
Since my program was already importing dateutil for dateutil.parser, I liked this solution best. It was as simple as: utcCurrentTime = datetime.datetime.now(tz=dateutil.tz.tzutc()). Viola!!Chuch
Thanks, @IanLee. The part that still puzzles me is that a method called utcfromtimestamp, which takes a POSIX timestamp as a parameter, which by definition is UTC, still produces a naïve datetime. It seems like a plain oversight to me.Saretta
C
16

To add timezone information in Python 3.2+

import datetime

>>> d = datetime.datetime.now(tz=datetime.timezone.utc)
>>> print(d.tzinfo)
'UTC+00:00'
Compline answered 2/9, 2017 at 9:42 Comment(3)
AttributeError: 'module' object has no attribute 'timezone' Python 2.7.13 (default, Jan 19 2017, 14:48:08)Isopod
@MarcinOwsiany try: from datetime import datetime, timezone then invoke with datetime.now(tz=timezone.utc)Madgemadhouse
This is the best way to get UTC timezone aware datetime object without using any third party python module like pytz. | Just to add one more thing. If you want to get LOCAL timezone aware datetime object without using any extra module then just add astimezone(): d.astimezone()Rockyrococo
Q
11

Julien Danjou wrote a good article explaining why you should never deal with timezones. An excerpt:

Indeed, Python datetime API always returns unaware datetime objects, which is very unfortunate. Indeed, as soon as you get one of this object, there is no way to know what the timezone is, therefore these objects are pretty "useless" on their own.

Alas, even though you may use utcnow(), you still won't see the timezone info, as you discovered.

Recommendations:

  • Always use aware datetime objects, i.e. with timezone information. That makes sure you can compare them directly (aware and unaware datetime objects are not comparable) and will return them correctly to users. Leverage pytz to have timezone objects.

  • Use ISO 8601 as the input and output string format. Use datetime.datetime.isoformat() to return timestamps as string formatted using that format, which includes the timezone information.

  • If you need to parse strings containing ISO 8601 formatted timestamps, you can rely on iso8601, which returns timestamps with correct timezone information. This makes timestamps directly comparable.

Quadrivalent answered 8/9, 2015 at 18:53 Comment(6)
This is slightly misleading recommendation. The rule of thumb is, never deal with timezones. Always store and transmit tz unware utc objects (epoch objects). Timezone should be only calculated at the time of representation in UIArchaeornis
That sounds like it already matches up with Julien's thoughts quite well. Which of his specific recommendations (as referenced above) are misleading?Welby
TZ unaware objects are not a problem if all datetime math is done in UTC. You should store and process all datetime in UTC and only convert datetime value to a specific TZ for a human when requested (i.e. at the UI layer).Morganica
I think why @Archaeornis called it misleading is more due to Julien's phrasing of "never deal with," since colloquially that's often used/interpreted as "ignore." Yet the essence of his recommendations is the opposite—that you in fact always need time zones on timestamps so you can normalize/store/compare accurately. So you still have to "deal with" them, just long enough to work out the UTC equivalent—and to Tom Scott's points, leaving the ugly mechanics of timezone conversions to things that already do that well instead of reinventing square wheels.Hospice
@BrianCline Do people still use stackoverflow ? 🤦‍♂️Archaeornis
@Archaeornis Yes, millions. A bit off topic for this question.Hospice
D
5

The behaviour of datetime.datetime.utcnow() returning UTC time as naive datetime object is obviously problematic and must be fixed. It can lead to unexpected result if your system local timezone is not UTC, since datetime library presume naive datetime object to represent system local time. For example, datetime.datetime.utcnow().timestaamp() gives timestamp of 4 hours ahead from correct value on my computer. Also, as of python 3.6, datetime.astimezone() can be called on naive datetime instances, but datetime.datetime.utcnow().astimezone(any_timezone) gives wrong result unless your system local timezone is UTC.

Dube answered 16/9, 2020 at 8:14 Comment(1)
those of us using datetime.datetime.utcnow() since it was ever introduced into Python aren't using tz-aware objects in most cases anyway - we're only ever storing and processing UTC and only doing the conversion for human users at the UI level. If you want UTC with tzinfo for some reason, only stick to the modern datetime objects that have tz support.Morganica
E
3

It should include and now() also. Related issue.

So till that, now() is preferred over today() and utcnow().

from datetime import datetime, timezone
utc = timezone.utc
date = datetime.now(utc)
print(date) # 2022-04-06 05:40:13.025347+00:00
Elatia answered 6/4, 2022 at 5:40 Comment(1)
Simplest and most accurate method today. The reason: date = datetime.now(timezone.utc) # datetime.datetime(2023, 10, 13, 8, 17, 21, 862183, tzinfo=datetime.timezone.utc) and date = datetime.utcnow() #datetime.datetime(2023, 10, 13, 8, 17, 35, 668704)Nomo
P
-7
from datetime import datetime 
from dateutil.relativedelta import relativedelta
d = datetime.now()
date = datetime.isoformat(d).split('.')[0]
d_month = datetime.today() + relativedelta(months=1)
next_month = datetime.isoformat(d_month).split('.')[0]
Pejsach answered 28/7, 2014 at 21:29 Comment(0)
F
-16

UTC dates don't need any timezone info since they're UTC, which by definition means that they have no offset.

Flattish answered 25/2, 2010 at 4:15 Comment(3)
As far as I can tell from docs.python.org/library/datetime.html, a datetime without a tzinfo is one where the time zone is unspecified. Here the time zone has been specified, so logically it should be present. There's a big difference between a date/time without an associated time zone and one which is definitely in UTC. (Ideally they should be different types IMO, but that's another matter...)Alberto
@JonSkeet I think you're missing Ignacio's point that UTC isn't a timezone. Amazing that this answer has -9 score as I type this...Dollfuss
@CS: Well Ignacio never stated that... and while strictly speaking UTC isn't a time zone, it's usually treated as one to make lives considerably simpler (including in Python, e.g. with pytz.utc). Note that there's a big difference between a value whose offset from UTC is unknown and one where it is known to be 0. The latter is what utcnow() should return, IMO. That would fit in with "An aware object is used to represent a specific moment in time that is not open to interpretation" as per the documentation.Alberto

© 2022 - 2024 — McMap. All rights reserved.