mindthief
mindthief

Reputation: 13383

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?

Upvotes: 521

Views: 683510

Answers (20)

Flimm
Flimm

Reputation: 150643

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.

Upvotes: 208

scrollout
scrollout

Reputation: 577

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

Upvotes: 0

G. Führ
G. Führ

Reputation: 400

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

Upvotes: 10

FObersteiner
FObersteiner

Reputation: 25554

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.

Upvotes: 9

AndiDog
AndiDog

Reputation: 70158

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:

Upvotes: 512

philfreo
philfreo

Reputation: 43804

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.

Upvotes: 198

Dariusz Walczak
Dariusz Walczak

Reputation: 4988

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

Upvotes: 20

cloudup
cloudup

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

Upvotes: -1

Jose
Jose

Reputation: 450

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)

Upvotes: 1

WJA
WJA

Reputation: 7004

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

Upvotes: 5

Mihai Capotă
Mihai Capotă

Reputation: 2471

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

Upvotes: 52

laffuste
laffuste

Reputation: 17095

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

Comment the following line in settings.py:

USE_TZ = True

Upvotes: 11

tmck-code
tmck-code

Reputation: 3198

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.

Upvotes: 1

Anupama V Iyengar
Anupama V Iyengar

Reputation: 176

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

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

Upvotes: -1

jfs
jfs

Reputation: 414205

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.

Upvotes: 22

ng10
ng10

Reputation: 1910

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.

Upvotes: 1

jcazor
jcazor

Reputation: 133

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

Upvotes: 9

jigar vagadiya
jigar vagadiya

Reputation: 1

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

Upvotes: -1

Antony Hatchkins
Antony Hatchkins

Reputation: 33984

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.

Upvotes: 1

user225312
user225312

Reputation: 131647

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.

Upvotes: 5

Related Questions