Handling timezone in Python

UTC, or Coordinated Universal Time, is the primary time standard by which the world regulates date and time.

  • astimezone() –> it helps to convert the time of a particular time zone into another time zone
  • localtime() –> it helps to get the current local time
import pytz
from datetime import datetime

print('The supported tz:', pytz.all_timezones, '\n')

Get country datetime

  • Show date-time for different timezone/country
datetime_ist = datetime.now(pytz.timezone('Asia/Kolkata'))
print("IST: ", datetime_ist.strftime("%m/%d/%Y, %H:%M:%S"))
# IST:  07/28/2021, 15:19:41

# UTC time --> IST-5.30
datetime_utc = datetime.now(pytz.timezone('UTC'))
# datetime_utc = datetime.now(timezone.utc)  # Alt way
print("UTC: ", datetime_utc.strftime("%m/%d/%Y, %H:%M:%S"))
# UTC:  07/28/2021, 09:49:41

# current date and time of NY
datetime_NY = datetime.now(pytz.timezone('America/New_York'))
print("NY:", datetime_NY.strftime("%m/%d/%Y, %H:%M:%S"))
# NY: 07/28/2021, 05:49:41

# current date and time of London
datetime_London = datetime.now(pytz.timezone('Europe/London'))
print("London:", datetime_London.strftime("%m/%d/%Y, %H:%M:%S"))
# London: 07/28/2021, 10:49:41

Timezone Conversion

  • format: old_tz_time.astimezone(new_tz)

Any timezone to UTC

datetime_NY = datetime.now(pytz.timezone('America/New_York'))
NY_to_utc = datetime_NY.astimezone(pytz.utc)
print("NY_to_utc: ", NY_to_utc.strftime("%m/%d/%Y, %H:%M:%S"))
# NY_to_utc:  07/28/2021, 09:49:41

datetime_ist = datetime.now(pytz.timezone('Asia/Kolkata'))
ist_to_utc = datetime_ist.astimezone(pytz.utc)
print("IST_to_utc: ", ist_to_utc.strftime("%m/%d/%Y, %H:%M:%S"))
# IST_to_utc:  07/28/2021, 09:49:41

UTC to any timezone

UTC_to_ist = datetime_utc.astimezone(pytz.timezone('Asia/Kolkata'))
print("UTC_to_ist: ",UTC_to_ist.strftime("%m/%d/%Y, %H:%M:%S"))
# UTC_to_ist: 07/28/2021, 15:19:41

Any to any timezone

datetime_NY = datetime.now(pytz.timezone('America/New_York'))
NY_to_London = datetime_NY.astimezone(pytz.timezone('Europe/London'))
print("NY_to_London:", NY_to_London.strftime("%m/%d/%Y, %H:%M:%S"))
# NY_to_London: 07/28/2021, 10:49:41

Timezone unaware/naive to Timezone aware

  • localize() –> Works on Naive
  • astimezone() –> Works on both Naive and Aware
  • replace()
# India naive to aware
ist = pytz.timezone('Asia/Kolkata')

naive_india = datetime.now()
# datetime.datetime(2021, 7, 28, 16, 56, 20, 516114)

aware_india_localize = ist.localize(naive_india)
# datetime.datetime(2021, 7, 28, 16, 56, 20, 516114, tzinfo=<DstTzInfo 'Asia/Kolkata' IST+5:30:00 STD>)

aware_india_astimezone = naive_india.astimezone(ist)
# datetime.datetime(2021, 7, 28, 16, 56, 20, 516114, tzinfo=<DstTzInfo 'Asia/Kolkata' IST+5:30:00 STD>)

# with replace()
aware_india_replace = naive_india.replace(tzinfo=ist)
# datetime.datetime(2021, 7, 28, 16, 56, 20, 516114, tzinfo=<DstTzInfo 'Asia/Kolkata' LMT+5:53:00 STD>)
# Naive to UTC
datetime.now()
# datetime.datetime(2022, 1, 20, 10, 27, 13, 264799)

pz_utc = pytz.timezone('utc')
pz_utc.localize(datetime.now())
# datetime.datetime(2022, 1, 20, 10, 27, 11, 91797, tzinfo=<UTC>)

pz_utc.localize(datetime(year=2022, month=1, day=14, hour=1, minute=0))
# datetime.datetime(2022, 1, 14, 1, 0, tzinfo=<UTC>)

Issue with replace

naive_india.replace(tzinfo=ist).astimezone(pytz.utc)
# datetime.datetime(2021, 7, 28, 11, 3, 20, 516114, tzinfo=<UTC>)

aware_india_localize.astimezone(pytz.utc)
# datetime.datetime(2021, 7, 28, 11, 26, 20, 516114, tzinfo=<UTC>)

print(ist)
# <DstTzInfo 'Asia/Kolkata' LMT+5:53:00 STD>
  • NOTE:
    • If you look carefully, the information about ist is LMT+5:53:00 STD
    • Which you might think is incorrect as the offset of IST from UTC is +5:30 and not 5:53
    • Initially IST was declared to be +5:53 ahead of UTC which was corrected later on to be +5:30.
    • But replace doesn’t really know about this
# Newer time
pytz.timezone('Asia/Kolkata').localize(datetime.now())
# datetime.datetime(2021, 7, 28, 17, 32, 48, 491126, tzinfo=<DstTzInfo 'Asia/Kolkata' IST+5:30:00 STD>)
# Old time
pytz.timezone('Asia/Kolkata').localize(datetime(1700,1,1))
# datetime.datetime(1700, 1, 1, 0, 0, tzinfo=<DstTzInfo 'Asia/Kolkata' LMT+5:53:00 STD>)
  • You can see the correct offset this time.
  • Whereas, for old_days, when localized to IST, the offset is 5:53.
  • Depending on the time, it returns the offset accordingly.
  • +5:53 for a really old date and +5:30 for a date after the time that the offset for IST was corrected.

Get Timezone info

from pytz import tzinfo
print(aware_india.tzinfo)  # Asia/Kolkata
print(naive_india.tzinfo)  # None

Gyan

Dos

  • Always use aware datetime objects.
  • When dealing with naive objects, use localize to add timezone
  • When you are converting timezones use astimezone.

Don’t

  • Create naive datetime objects.
  • Use datetime.datetime.replace for adding or manipulating timezone information on a datetime object.
  • Except when you use it to instantiate a datetime object in UTC.

Reference