What I need to do
I have a timezone-unaware datetime object, to which I need to add a time zone in order to be able to compare it with other timezon
Changing between timezones
import pytz
from datetime import datetime
other_tz = pytz.timezone('Europe/Madrid')
# From random aware datetime...
aware_datetime = datetime.utcnow().astimezone(other_tz)
>> 2020-05-21 08:28:26.984948+02:00
# 1. Change aware datetime to UTC and remove tzinfo to obtain an unaware datetime
unaware_datetime = aware_datetime.astimezone(pytz.UTC).replace(tzinfo=None)
>> 2020-05-21 06:28:26.984948
# 2. Set tzinfo to UTC directly on an unaware datetime to obtain an utc aware datetime
aware_datetime_utc = unaware_datetime.replace(tzinfo=pytz.UTC)
>> 2020-05-21 06:28:26.984948+00:00
# 3. Convert the aware utc datetime into another timezone
reconverted_aware_datetime = aware_datetime_utc.astimezone(other_tz)
>> 2020-05-21 08:28:26.984948+02:00
# Initial Aware Datetime and Reconverted Aware Datetime are equal
print(aware_datetime1 == aware_datetime2)
>> True
quite new to Python and I encountered the same issue. I find this solution quite simple and for me it works fine (Python 3.6):
unaware=parser.parse("2020-05-01 0:00:00")
aware=unaware.replace(tzinfo=tz.tzlocal()).astimezone(tz.tzlocal())
In general, to make a naive datetime timezone-aware, use the localize method:
import datetime
import pytz
unaware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0)
aware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0, pytz.UTC)
now_aware = pytz.utc.localize(unaware)
assert aware == now_aware
For the UTC timezone, it is not really necessary to use localize
since there is no daylight savings time calculation to handle:
now_aware = unaware.replace(tzinfo=pytz.UTC)
works. (.replace
returns a new datetime; it does not modify unaware
.)
Use dateutil.tz.tzlocal() to get the timezone in your usage of datetime.datetime.now() and datetime.datetime.astimezone():
from datetime import datetime
from dateutil import tz
unlocalisedDatetime = datetime.now()
localisedDatetime1 = datetime.now(tz = tz.tzlocal())
localisedDatetime2 = datetime(2017, 6, 24, 12, 24, 36, tz.tzlocal())
localisedDatetime3 = unlocalisedDatetime.astimezone(tz = tz.tzlocal())
localisedDatetime4 = unlocalisedDatetime.replace(tzinfo = tz.tzlocal())
Note that datetime.astimezone
will first convert your datetime
object to UTC then into the timezone, which is the same as calling datetime.replace
with the original timezone information being None
.
In the format of unutbu's answer; I made a utility module that handles things like this, with more intuitive syntax. Can be installed with pip.
import datetime
import saturn
unaware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0)
now_aware = saturn.fix_naive(unaware)
now_aware_madrid = saturn.fix_naive(unaware, 'Europe/Madrid')
This codifies @Sérgio and @unutbu's answers. It will "just work" with either a pytz.timezone
object or an IANA Time Zone string.
def make_tz_aware(dt, tz='UTC', is_dst=None):
"""Add timezone information to a datetime object, only if it is naive."""
tz = dt.tzinfo or tz
try:
tz = pytz.timezone(tz)
except AttributeError:
pass
return tz.localize(dt, is_dst=is_dst)
This seems like what datetime.localize()
(or .inform()
or .awarify()
) should do, accept both strings and timezone objects for the tz argument and default to UTC if no time zone is specified.