526

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?

5
  • related: How do I get the UTC time of “midnight” for a given timezone? Commented Feb 10, 2013 at 11:14
  • 64
    Seems like we can use datetime.now().astimezone() since Python 3.6 Commented Jul 28, 2017 at 7:02
  • 1
    Thanks @johnchen902. Your comment has been converted to an answer here: stackoverflow.com/a/49059780/247696 Commented Sep 16, 2020 at 7:53
  • 4
    @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() Commented Jan 16, 2021 at 6:23
  • 4
    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. Commented Jun 4, 2021 at 8:39

20 Answers 20

514

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:

Sign up to request clarification or add additional context in comments.

19 Comments

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)
|
213

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

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

>>> import datetime >>> 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. If the operating system doesn't have an IANA database of time zones, (notably, Windows doesn't), then the information is retrieved from the first-party PyPI package tzdata if installed.

In Python ≥ 3.2:

If you need to specify UTC as the time zone, the standard library provides support from this in Python 3.2 or later:

>>> 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 or later, you can shorten the last line to:

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

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

3 Comments

There's also datetime.datetime.now(tz=datetime.timezone(datetime.timedelta(seconds=3600))) which can utilize timedelta to specify the tz parameter to now().
@Torxed That's fascinating. That would give you a fixed offset time zone. I would prefer to use a time zone linked to a location or political area like America/Los_Angeles, as the latter takes into account daylight saving time.
I guess you could use .tzname together with .dst for that and feed it to tz=. There's an example of a custom class too. But yea, ZoneInfo is neat, just thought I'd point people to a builtin that does the same thing (more or less *inconveniently)*
198

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.

8 Comments

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.
|
52

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')) 

7 Comments

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.
@SauravKumar, that is not the original question. Try datetime.now(timezone.utc).
Is there a catch to this? It's bizarre to me that this is hardly mentioned in the official documentation or anywhere else on the internet. "Aware" datetimes are obviously preferred over naive ones, so why isn't this the default way to get the current datetime with Python? If this is possible with the standard library since Python 3.3, why doesn't datetime.now() just return an aware object in the local timezone in the first place?
|
23

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.

2 Comments

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.
20

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 

22 Comments

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.
|
11

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

Comment the following line in settings.py:

USE_TZ = True 

2 Comments

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.
10

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()) 

To install python-dateutil as a dependency, run:

pip install python-dateutil 

3 Comments

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!
9

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

2 Comments

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
9

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, don't expect any "DST-awareness" from a timedelta timezone.

Comments

6

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.

1 Comment

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

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 

Comments

1

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.

5 Comments

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!
1

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.

2 Comments

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.
1

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.

Comments

0

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) 

Comments

0

I tried most of the other solutions here, and they don't work. Here is something that works on Python3.x

import datetime, pytz def get_current_datetime(timezone_str='America/Toronto'): utc_now = datetime.datetime.now() timezone = pytz.timezone(timezone_str) now_tz = utc_now.replace(tzinfo=pytz.utc).astimezone(timezone) return now_tz 

Comments

-1

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>) 

Comments

-2

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")) 

Comments

-2

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

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

1 Comment

This only applies to Django apps.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.