ChatGPT解决这个技术问题 Extra ChatGPT

How do I get a value of datetime.today() in Python that is "timezone aware"?

I am trying to subtract one date value from the value of datetime.datetime.today() to calculate how long ago something was. But it complains:

TypeError: can't subtract offset-naive and offset-aware datetimes

The return value from datetime.datetime.today() doesn't seem to be "timezone aware", while my other date value is. How do I get a return value from datetime.datetime.today() that is timezone aware?

The ideal solution would be for it to automatically know the timezone.

Right now, it's giving me the time in local time, which happens to be PST, i.e. UTC - 8 hours. Worst case, is there a way I can manually enter a timezone value into the datetime object returned by datetime.datetime.today() and set it to UTC-8?

Seems like we can use datetime.now().astimezone() since Python 3.6
Thanks @johnchen902. Your comment has been converted to an answer here: stackoverflow.com/a/49059780/247696
@johnchen's answer autotomatically gets the answer in the local time zone. To get the same answer as far back as Python 3.3: use from datetime import datetime, timezone; datetime.now(timezone.utc).astimezone()
Note that datetime.date objects can't have an associated time zone, only datetime.datetime objects can. So the question is about datetime.datetime.today, and not about datetime.date.today, which are different. I've edited the question to make this slightly clearer.

F
Flimm

In the standard library, there is no cross-platform way to create aware timezones without creating your own timezone class. (Edit: Python 3.9 introduces zoneinfo in the standard library which does provide this functionality.)

On Windows, there's win32timezone.utcnow(), but that's part of pywin32. I would rather suggest to use the pytz library, which has a constantly updated database of most timezones.

Working with local timezones can be very tricky (see "Further reading" links below), so you may rather want to use UTC throughout your application, especially for arithmetic operations like calculating the difference between two time points.

You can get the current date/time like so:

import pytz
from datetime import datetime
datetime.utcnow().replace(tzinfo=pytz.utc)

Mind that datetime.today() and datetime.now() return the local time, not the UTC time, so applying .replace(tzinfo=pytz.utc) to them would not be correct.

Another nice way to do it is:

datetime.now(pytz.utc)

which is a bit shorter and does the same.

Further reading/watching why to prefer UTC in many cases:

pytz documentation

What Every Developer Should Know About Time – development hints for many real-life use cases

The Problem with Time & Timezones - Computerphile – funny, eye-opening explanation about the complexity of working with timezones (video)


How about datetime.now(pytz.utc) instead of datetime.utcnow().replace(tzinfo = pytz.utc) ?
now(utc) doesn't return today (unless it is midnight in UTC), it returns the current time in UTC. You need also .replace(hour=0, minute=0, ...) to get the beginning of the day (like datetime.today())
The docs say that today() returns the current time, not midnight. If there is a use case where midnight is required, yes, the replacement needs to be done accordingly. Since the original question was about datetime difference, I don't think that midnight is required.
Adding to this answer, if you happen to be using django, always use timezone.now() instead of datetime.now() since it will use UTC automatically if USE_TZ = True. timezone is locating at django.utils.timezone, documentation: docs.djangoproject.com/en/1.11/topics/i18n/timezones
In python3 you can simply use datetime.now(timezone.utc)
F
Flimm

Get the current time, in a specific timezone:

import datetime
import pytz
my_date = datetime.datetime.now(pytz.timezone('US/Pacific'))

Remember to install pytz first.


See this.
you should NOT use localized time except for output. Many things go wrong when using timezone based datetime: a simple timedelta does not take daylight saving into account unless you are in UTC time to begin with. Always use timezone aware based on UTC. convert to local timezone on output when needed.
To reiterate a disagreement with @MrE that I've previously voiced in the comments on the accepted answer: there are perfectly valid reasons to work with localised datetimes and "you should NOT use localized time except for output" is overly broad advice. Suppose you're adding 1 day to a datetime a few hours before a daylight savings boundary at which the clocks go back by an hour. What result do you want? If you think the time should be the same, use localised datetimes. If you think it should be an hour earlier, use UTC or timezone-naive datetimes. Which makes sense is domain-dependent.
@MarkAmery as much as I can agree that you may want to ADD or SUBTRACT a number of days or hours and not care about timezone issues (like your example), this comment pertains to passing timezone corrected time to a client. Since Python is primarily used for back end processes, it passes times to a client. The server should always pass date/time in UTC and the client should convert it to its own local date/time/timezone, otherwise bad things happen: just check the output of datetime.datetime(2016, 11, 5, 9, 43, 45, tzinfo=pytz.timezone('US/Pacific')) and see if that's what you expected
"The server should always pass date/time in UTC and the client should convert it to its own local date/time/timezone" - no, this isn't universally true. Sometimes using the client's timezone is inappropriate and the appropriate timezone needs to be transmitted as part of the data. If, as a Londoner, I view the meeting times of a San Francisco chess club on their website, I should see them in San Francisco time, not in London time.
F
Flimm

In Python 3.2+: datetime.timezone.utc:

The standard library makes it much easier to specify UTC as the time zone:

>>> import datetime
>>> datetime.datetime.now(datetime.timezone.utc)
datetime.datetime(2020, 11, 27, 14, 34, 34, 74823, tzinfo=datetime.timezone.utc)

You can also get a datetime that includes the local time offset using astimezone:

>>> datetime.datetime.now(datetime.timezone.utc).astimezone()
datetime.datetime(2020, 11, 27, 15, 34, 34, 74823, tzinfo=datetime.timezone(datetime.timedelta(seconds=3600), 'CET'))

(In Python 3.6+, you can shorten the last line to: datetime.datetime.now().astimezone())

If you want a solution that uses only the standard library and that works in both Python 2 and Python 3, see jfs' answer.

In Python 3.9+: zoneinfo to use the IANA time zone database:

In Python 3.9, you can specify particular time zones using the standard library, using zoneinfo, like this:

>>> from zoneinfo import ZoneInfo
>>> datetime.datetime.now(ZoneInfo("America/Los_Angeles"))
datetime.datetime(2020, 11, 27, 6, 34, 34, 74823, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles'))

zoneinfo gets its database of time zones from the operating system, or from the first-party PyPI package tzdata if available.


M
Mihai Capotă

A one-liner using only the standard library works starting with Python 3.3. You can get a local timezone aware datetime object using astimezone (as suggested by johnchen902):

from datetime import datetime, timezone

aware_local_now = datetime.now(timezone.utc).astimezone()

print(aware_local_now)
# 2020-03-03 09:51:38.570162+01:00

print(repr(aware_local_now))
# datetime.datetime(2020, 3, 3, 9, 51, 38, 570162, tzinfo=datetime.timezone(datetime.timedelta(0, 3600), 'CET'))

The documentation is a big help, found here: docs.python.org/3.8/library/… . This incredibly basic piece of functionality is buried deep in an obscure paragraph in the docs, so that this stackoverflow answer is effectively the only place on the entire internet with this information. In the documentation, it can also be seen that, since Python 3.6, datetime.now() can be called without any arguments and return the correct local result (naive datetimes are presumed to be in the local time zone).
Passing timezone.utc to now is not needed here, you can just run datetime.datetime.now().astimezone()
@Flimm, it is needed if you want to support Python <3.6.
str(datetime.now(timezone.utc).astimezone()) -> '2020-09-18 19:19:33.508176+05:30' | Is there any way I get the date object with UTC timezone? It's always giving the timezone as IST. I don't want to use any other python library like pytz
@SauravKumar, that is not the original question. Try datetime.now(timezone.utc).
P
Peter Mortensen

Here's a stdlib solution that works on both Python 2 and 3:

from datetime import datetime

now = datetime.now(utc) # Timezone-aware datetime.utcnow()
today = datetime(now.year, now.month, now.day, tzinfo=utc) # Midnight

where today is an aware datetime instance representing the beginning of the day (midnight) in UTC and utc is a tzinfo object (example from the documentation):

from datetime import tzinfo, timedelta

ZERO = timedelta(0)

class UTC(tzinfo):
    def utcoffset(self, dt):
        return ZERO

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

    def dst(self, dt):
        return ZERO

utc = UTC()

Related: performance comparison of several ways to get midnight (start of a day) for a given UTC time. Note: it is more complex, to get midnight for a time zone with a non-fixed UTC offset.


In 3.9+, this is moved into the Std Lib See @zimm's answer below!
@Marc: utc timezone is available in stdlib since Python 3.2 and [almost] functional local timezone since Python 3.3. Python 3.9 introduces zoneinfo (many other timezones) into stdlib [that is great] but you don't need it in this case. See @Flimm's answer.
F
Flimm

Another method to construct time zone aware datetime object representing current time:

import datetime
import pytz

pytz.utc.localize( datetime.datetime.utcnow() )  

You can install pytz from PyPI by running:

$ pipenv install pytz

note that pytz.utc and pytz.UTC are both defined (and are the same)
This answer is better than the accepted one since it is more universal: replace()ing timezone is generally error-prone in most other uses, while localize()ing is the preferred way of assigning timezone to naive timestamps.
@AntonyHatchkins: .localize() method fails for ambiguous local times (non-utc input). @philfreo's answer that uses .now(pytz_timezone) continues to work in such cases.
As specified in python docs, .now(pytz_timezone) does exactly the same as localize(utcnow) - first it generates current time in UTC, then it assigns it a timezone: "<...>In this case the result is equivalent to tz.fromutc(datetime.utcnow().replace(tzinfo=tz))". Both answers are correct and work always.
The only naive (non-utc) time that can be safely made timezone aware is now : the underlying system is supposed to know the UTC value and pytz through OLSON db is supposed to know how to convert it to any timezone in the world. Making any other naive (non-utc) time timezone aware is difficult because of the ambiguity during daylight saving shifts. That's not a problem of .localize (feeding it a is_dst value makes it work for any date). That's an inherent problem of daylight saving practice.
P
Peter Mortensen

Use dateutil as described in Python datetime.datetime.now() that is timezone aware:

from dateutil.tz import tzlocal
# Get the current date/time with the timezone.
now = datetime.datetime.now(tzlocal())

See this answer by J.F. Sebastian for a situation where this gives incorrect result.
I think the error in the other post is only relevant in specific use cases. The tzlocal() function still is one of the simplest solutions and should definitely be mentioned here.
In 3.9+, this is moved into the Std Lib See @zimm's answer below!
l
laffuste

If you are using Django, you can set dates non-tz aware (only UTC).

Comment the following line in settings.py:

USE_TZ = True

where did you see django mentioned in this question?
Some kind soul deleted my previous comment-apology here, so again: shame on me, wrong answer as the question is not Django-specific. I left it because it might help some users anyway but I will delete it when the score approaches 0. If this answer is inappropriate, feel free to downvote.
j
jcazor

Here is one way to generate it with the stdlib:

import time
from datetime import datetime

FORMAT='%Y-%m-%dT%H:%M:%S%z'
date=datetime.strptime(time.strftime(FORMAT, time.localtime()),FORMAT)

date will store the local date and the offset from UTC, not the date at UTC timezone, so you can use this solution if you need to identify which timezone the date is generated at. In this example and in my local timezone:

date
datetime.datetime(2017, 8, 1, 12, 15, 44, tzinfo=datetime.timezone(datetime.timedelta(0, 7200)))

date.tzname()
'UTC+02:00'

The key is adding the %z directive to the representation FORMAT, to indicate the UTC offset of the generated time struct. Other representation formats can be consulted in the datetime module docs

If you need the date at the UTC timezone, you can replace time.localtime() with time.gmtime()

date=datetime.strptime(time.strftime(FORMAT, time.gmtime()),FORMAT)

date    
datetime.datetime(2017, 8, 1, 10, 23, 51, tzinfo=datetime.timezone.utc)

date.tzname()
'UTC'

Edit

This works only on python3. The z directive is not available on python 2 _strptime.py code


ValueError: 'z' is a bad directive in format '%Y-%m-%dT%H:%M:%S%z'
You are on python 2, right? Unfortunately, It seems the z directive is not available on python 2. _strptime.py code
C
Community

pytz is a Python library that allows accurate and cross platform timezone calculations using Python 2.3 or higher.

With the stdlib, this is not possible.

See a similar question on SO.


In 3.9+, this is moved into the Std Lib See @zimm's answer below!
F
FObersteiner

It should be emphasized that since Python 3.6, you only need the standard lib to get a timezone aware datetime object that represents local time (the setting of your OS). Using astimezone()

import datetime

datetime.datetime(2010, 12, 25, 10, 59).astimezone()
# e.g.
# datetime.datetime(2010, 12, 25, 10, 59, tzinfo=datetime.timezone(datetime.timedelta(seconds=3600), 'Mitteleuropäische Zeit'))

datetime.datetime(2010, 12, 25, 12, 59).astimezone().isoformat()
# e.g.
# '2010-12-25T12:59:00+01:00'

# I'm on CET/CEST

(see @johnchen902's comment). Note there's a small caveat though, astimezone(None) gives aware datetime, unaware of DST.


J
JohnAndrews

Here is a solution using a readable timezone and that works with today():

from pytz import timezone

datetime.now(timezone('Europe/Berlin'))
datetime.now(timezone('Europe/Berlin')).today()

You can list all timezones as follows:

import pytz

pytz.all_timezones
pytz.common_timezones # or

C
Community

Getting a timezone-aware date in utc timezone is enough for date subtraction to work.

But if you want a timezone-aware date in your current time zone, tzlocal is the way to go:

from tzlocal import get_localzone  # pip install tzlocal
from datetime import datetime
datetime.now(get_localzone())

PS dateutil has a similar function (dateutil.tz.tzlocal). But inspite of sharing the name it has a completely different code base, which as noted by J.F. Sebastian can give wrong results.


python is usually used on the server. Local time zone on a server is usually pointless and should always be set to UTC. Setting datetime tzinfo this way fails in some cases. better use UTC, then localize to the wanted timezone only on output. any timedelta computation for example doesn't consider daylight saving, so these should be done in UTC, then localized.
@MrE Wrong, offtopic, examples?
try using a datetime object localized in a timezone that observes daylight saving, add a number of days to change daylight saving state, and you'll see that operating on datetime objects in localized timezone fails and won't respect daylight saving. Hence my comment that you should ALWAYS do any datetime operation in UTC time.
the point being: don't do this, do your operations in UTC, and then use datetime.astimezone(timezone) to convert to the localtime zone on output.
In 3.9+, this is moved into the Std Lib See @zimm's answer below!
n
ng10

Another alternative, in my mind a better one, is using Pendulum instead of pytz. Consider the following simple code:

>>> import pendulum

>>> dt = pendulum.now().to_iso8601_string()
>>> print (dt)
2018-03-27T13:59:49+03:00
>>>

To install Pendulum and see their documentation, go here. It have tons of options (like simple ISO8601, RFC3339 and many others format support), better performance and tend to yield simpler code.


not sure why the vote down here, this code is working in multiple programs that run 7/24 for me :). not that i mind other opinion, but please say why it is not working for you, to allow me to check it. Thanks in advance
this is a great suggestion, in this messy field like date time manipulation readability and ease of use comes first, IMO.
P
Peter Mortensen

Especially for non-UTC timezones:

The only timezone that has its own method is timezone.utc, but you can fudge a timezone with any UTC offset if you need to by using timedelta & timezone, and forcing it using .replace.

In [1]: from datetime import datetime, timezone, timedelta

In [2]: def force_timezone(dt, utc_offset=0):
   ...:     return dt.replace(tzinfo=timezone(timedelta(hours=utc_offset)))
   ...:

In [3]: dt = datetime(2011,8,15,8,15,12,0)

In [4]: str(dt)
Out[4]: '2011-08-15 08:15:12'

In [5]: str(force_timezone(dt, -8))
Out[5]: '2011-08-15 08:15:12-08:00'

Using timezone(timedelta(hours=n)) as the time zone is the real silver bullet here, and it has lots of other useful applications.


J
Jose

Tyler from 'howchoo' made a really great article that helped me get a better idea of the Datetime Objects, link below

Working with Datetime

essentially, I just added the following to the end of both my datetime objects

.replace(tzinfo=pytz.utc)

Example:

import pytz
import datetime from datetime

date = datetime.now().replace(tzinfo=pytz.utc)

j
jigar vagadiya

If you get current time and date in python then import date and time,pytz package in python after you will get current date and time like as..

from datetime import datetime
import pytz
import time
str(datetime.strftime(datetime.now(pytz.utc),"%Y-%m-%d %H:%M:%S%t"))

P
Peter Mortensen

Use the timezone as shown below for a timezone-aware date time. The default is UTC:

from django.utils import timezone
today = timezone.now()

This only applies to Django apps.
c
cloudup

try pnp_datetime, all the time been used and returned is with timezone, and will not cause any offset-naive and offset-aware issues.

>>> from pnp_datetime.pnp_datetime import Pnp_Datetime
>>>
>>> Pnp_Datetime.utcnow()
datetime.datetime(2020, 6, 5, 12, 26, 18, 958779, tzinfo=<UTC>)