7.10. Datetime Timezone
Always keep dates and times only in UTC (important!)
Datetimes should be converted to local time only when displaying to user
Computerphile Time & Time Zones [3]
Abolition of Time Zones [1]
Since Python 3.9: PEP 615 -- Support for the IANA Time Zone Database in the Standard Library
pip install tzdata
7.10.1. SetUp
>>> from datetime import date, time, datetime, timedelta, timezone
>>> from zoneinfo import ZoneInfo
7.10.2. Daylight Saving Time
Daylight Saving Time date is different for each country and even US state
Australia is 9h 30m shifted
India is 3h 30m shifted
Nepal is 3h 45m shifted
In southern hemisphere the Daylight Saving Time is opposite direction
They subtract hour in March and add in October
Samoa is on the international date line
Samoa changed from UTC-1200 to UTC+1200 for easier trades with Australia
During World War II England was GMT+0200
Libya in 2013 discontinued DST with couple of days notice
Israel is on a different timezone than Palestine (multiple timezones in one location, based on nationality)
Change from Julian to Gregorian calendar caused to skip few weeks
In 18th century World change from Julian to Gregorian calendar
In 20th century Russia change from Julian to Gregorian calendar (different days which was skipped than for worldwide change)
In britain until 16th century the year started on 25th of March
Mind leap seconds (add, subtract)
UTC includes leap seconds
Astronomical time does not include leap seconds
Google invented smear second (on the day of leap second) they add a small fraction of a second to each second that day until midnight
Not all cities has DST https://www.timeanddate.com/time/us/arizona-no-dst.html
Comparing datetime works only when all has the same timezone (UTC):
7.10.3. Timezone Naive Datetimes
>>> datetime(1957, 10, 4, 19, 28, 34)
datetime.datetime(1957, 10, 4, 19, 28, 34)
>>> datetime.now()
datetime.datetime(1957, 10, 4, 19, 28, 34)
7.10.4. Timezone Aware Datetimes
>>> datetime.now(timezone.utc)
datetime.datetime(1957, 10, 4, 19, 28, 34, tzinfo=datetime.timezone.utc)
>>> datetime(1957, 10, 4, 19, 28, 34, tzinfo=timezone.utc)
datetime.datetime(1957, 10, 4, 19, 28, 34, tzinfo=datetime.timezone.utc)
>>> dt = datetime(1957, 10, 4, 19, 28, 34)
>>> dt.replace(tzinfo=timezone.utc)
datetime.datetime(1957, 10, 4, 19, 28, 34, tzinfo=datetime.timezone.utc)
7.10.5. UTCNow
datetime.utcnow()
produces timezone naive datetimes!
>>> datetime.utcnow()
datetime.datetime(1957, 10, 4, 17, 28, 34)
>>> datetime.utcnow(tz=timezone.utc)
Traceback (most recent call last):
TypeError: datetime.utcnow() takes no keyword arguments
>>> datetime.utcnow(timezone.utc)
Traceback (most recent call last):
TypeError: datetime.utcnow() takes no arguments (1 given)
7.10.6. IANA Time Zone Database
https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
pip install tzdata
IANA 2017a timezone database [2]:
7.10.7. ZoneInfo
Since Python 3.9: PEP 615 -- Support for the IANA Time Zone Database in the Standard Library
>>> utc = ZoneInfo('UTC')
>>> est = ZoneInfo('US/Eastern')
>>> cet = ZoneInfo('Europe/Warsaw')
>>> alm = ZoneInfo('Asia/Almaty')
Working with ZoneInfo
objects:
>>> dt = datetime(1969, 7, 21, 2, 56, 15, tzinfo=ZoneInfo('UTC'))
>>> print(dt)
1969-07-21 02:56:15+00:00
>>> dt += timedelta(days=7)
>>> print(dt)
1969-07-28 02:56:15+00:00
ZoneInfo
objects knows Daylight Saving Time:
>>> dt = datetime(2000, 1, 1, tzinfo=ZoneInfo('America/Los_Angeles')) # Daylight saving time
>>>
>>> dt.tzname()
'PST'
>>>
>>> dt += timedelta(days=100) # Standard time
>>> dt.tzname()
'PDT'
7.10.8. Use Case - 1
>>> from datetime import datetime
>>> from zoneinfo import ZoneInfo
>>>
>>>
>>> UTC = ZoneInfo('UTC')
>>> BAJKONUR = ZoneInfo('Asia/Almaty')
>>> WAW = ZoneInfo('Europe/Warsaw')
>>> LOS_ANGELES = ZoneInfo('America/Los_Angeles')
>>>
>>>
>>> dt = datetime(1961, 4, 12, 6, 7, tzinfo=UTC)
>>> dt
datetime.datetime(1961, 4, 12, 6, 7, tzinfo=zoneinfo.ZoneInfo(key='UTC'))
>>>
>>> dt.astimezone(BAJKONUR)
datetime.datetime(1961, 4, 12, 12, 7, tzinfo=zoneinfo.ZoneInfo(key='Asia/Almaty'))
>>>
>>> dt.astimezone(WAW)
datetime.datetime(1961, 4, 12, 7, 7, tzinfo=zoneinfo.ZoneInfo(key='Europe/Warsaw'))
>>>
>>> dt.astimezone(LOS_ANGELES)
datetime.datetime(1961, 4, 11, 22, 7, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles'))
7.10.9. Use Case - 2
Descriptor Timezone Converter:
>>> from dataclasses import dataclass
>>> from datetime import datetime
>>> from zoneinfo import ZoneInfo
>>>
>>>
>>> class Timezone:
... def __init__(self, name):
... self.timezone = ZoneInfo(name)
...
... def __get__(self, parent, *args):
... utc = parent.utc.replace(tzinfo=ZoneInfo('UTC'))
... return utc.astimezone(self.timezone)
...
... def __set__(self, parent, new_datetime):
... local_time = new_datetime.replace(tzinfo=self.timezone)
... parent.utc = local_time.astimezone(ZoneInfo('UTC'))
>>>
>>>
>>> @dataclass
... class Time:
... utc = datetime.now(tz=ZoneInfo('UTC'))
... warsaw = Timezone('Europe/Warsaw')
... eastern = Timezone('America/New_York')
... pacific = Timezone('America/Los_Angeles')
>>>
>>>
>>> t = Time()
>>>
>>> # Gagarin's launch to space
>>> t.utc = datetime(1961, 4, 12, 6, 7)
>>>
>>> print(t.utc)
1961-04-12 06:07:00
>>> print(t.warsaw)
1961-04-12 07:07:00+01:00
>>> print(t.eastern)
1961-04-12 01:07:00-05:00
>>> print(t.pacific)
1961-04-11 22:07:00-08:00
>>>
>>>
>>> # Armstrong's first Lunar step
>>> t.warsaw = datetime(1969, 7, 21, 3, 56, 15)
>>>
>>> print(t.utc)
1969-07-21 02:56:15+00:00
>>> print(t.warsaw)
1969-07-21 03:56:15+01:00
>>> print(t.eastern)
1969-07-20 22:56:15-04:00
>>> print(t.pacific)
1969-07-20 19:56:15-07:00
7.10.10. References
7.10.11. Assignments
# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`
# %% About
# - Name: Datetime Timezone ZoneInfo
# - Difficulty: easy
# - Lines: 8
# - Minutes: 8
# %% English
# 0. If you're on Windows then install module `tzdata` (`pip install tzdata`)
# 1. Create `zoneinfo.ZoneInfo` object of:
# - UTC
# - London, United Kingdom
# - Buenos Aires, Argentina
# - Warsaw, Poland
# - Tokyo, Japan
# - Sydney, Australia
# - Auckland, New Zealand
# - New York, USA
# 2. Use `List of tz database time zones` [1]
# 3. Run doctests - all must succeed
# %% Polish
# 0. Jeżeli masz Windows to zainstaluj moduł `tzdata` (`pip install tzdata`)
# 1. Stwórz obiekt `zoneinfo.ZoneInfo` z:
# - UTC
# - London, Wielka Brytania
# - Buenos Aires, Argentyna
# - Warsaw, Polska
# - Tokyo, Japan
# - Sydney, Australia
# - Auckland, Nowa Zelandia
# - New York, USA
# 2. Użyj `List of tz database time zones` [1]
# 3. Uruchom doctesty - wszystkie muszą się powieść
# %% References
# [1] Wikipedia. List of tz database time zones.
# Retrieved: 2022-12-01
# Year: 2022
# URL: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# [2] IANA. Time Zone Database.
# Retrieved: 2022-12-01
# Year: 2022
# URL: https://data.iana.org/time-zones/releases/
# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> from datetime import datetime
>>> assert utc is not Ellipsis, \
'Assign value to variable `utc` has instead of Ellipsis (...)'
>>> assert london is not Ellipsis, \
'Assign value to variable `london` instead of Ellipsis (...)'
>>> assert buenos_aires is not Ellipsis, \
'Assign value to variable `buenos_aires` instead of Ellipsis (...)'
>>> assert warsaw is not Ellipsis, \
'Assign value to variable `warsaw` instead of Ellipsis (...)'
>>> assert tokyo is not Ellipsis, \
'Assign value to variable `tokyo` instead of Ellipsis (...)'
>>> assert sydney is not Ellipsis, \
'Assign value to variable `sydney` instead of Ellipsis (...)'
>>> assert auckland is not Ellipsis, \
'Assign value to variable `auckland` instead of Ellipsis (...)'
>>> assert new_york is not Ellipsis, \
'Assign value to variable `new_york` instead of Ellipsis (...)'
>>> assert isinstance(utc, ZoneInfo), \
'Variable `utc` has invalid type, must be a ZoneInfo'
>>> assert isinstance(london, ZoneInfo), \
'Variable `london` has invalid type, must be a ZoneInfo'
>>> assert isinstance(buenos_aires, ZoneInfo), \
'Variable `buenos_aires` has invalid type, must be a ZoneInfo'
>>> assert isinstance(warsaw, ZoneInfo), \
'Variable `warsaw` has invalid type, must be a ZoneInfo'
>>> assert isinstance(tokyo, ZoneInfo), \
'Variable `tokyo` has invalid type, must be a ZoneInfo'
>>> assert isinstance(sydney, ZoneInfo), \
'Variable `sydney` has invalid type, must be a ZoneInfo'
>>> assert isinstance(auckland, ZoneInfo), \
'Variable `auckland` has invalid type, must be a ZoneInfo'
>>> assert isinstance(new_york, ZoneInfo), \
'Variable `new_york` has invalid type, must be a ZoneInfo'
>>> def utcoffset(tz: ZoneInfo):
... dt = datetime(2000, 1, 1)
... return tz.utcoffset(dt).total_seconds()
>>>
>>> assert utcoffset(utc) == 0, \
'Invalid time zone, check IANA Time Zone Database'
>>> assert utcoffset(london) == 0, \
'Invalid time zone, check IANA Time Zone Database'
>>> assert utcoffset(buenos_aires) == -10800.0, \
'Invalid time zone, check IANA Time Zone Database'
>>> assert utcoffset(warsaw) == 3600, \
'Invalid time zone, check IANA Time Zone Database'
>>> assert utcoffset(tokyo) == 32400, \
'Invalid time zone, check IANA Time Zone Database'
>>> assert utcoffset(sydney) == 39600, \
'Invalid time zone, check IANA Time Zone Database'
>>> assert utcoffset(auckland) == 46800, \
'Invalid time zone, check IANA Time Zone Database'
>>> assert utcoffset(new_york) == -18000.0, \
'Invalid time zone, check IANA Time Zone Database'
"""
from zoneinfo import ZoneInfo
# Timezone in UTC
# type: ZoneInfo
utc = ...
# Timezone in London, United Kingdom
# type: ZoneInfo
london = ...
# Timezone in Buenos Aires, Argentina
# type: ZoneInfo
buenos_aires = ...
# Timezone in Warsaw, Poland
# type: ZoneInfo
warsaw = ...
# Timezone in Tokyo, Japan
# type: ZoneInfo
tokyo = ...
# Timezone in Sydney, Australia
# type: ZoneInfo
sydney = ...
# Timezone in Auckland, New Zealand
# type: ZoneInfo
auckland = ...
# Timezone in New York, USA
# type: ZoneInfo
new_york = ...
# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`
# %% About
# - Name: Datetime Timezone ZoneInfo
# - Difficulty: easy
# - Lines: 3
# - Minutes: 5
# %% English
# 0. If you're on Windows then install module `tzdata` (`pip install tzdata`)
# 1. Create `zoneinfo.ZoneInfo` object of:
# - Cape Canaveral, FL, USA
# - Houston, TX, USA
# - Bajkonur Cosmodrome, Kazachstan
# 2. Use `List of tz database time zones` [1]
# 3. Run doctests - all must succeed
# %% Polish
# 0. Jeżeli masz Windows to zainstaluj moduł `tzdata` (`pip install tzdata`)
# 1. Stwórz obiekt `zoneinfo.ZoneInfo` z:
# - Cape Canaveral, FL, USA
# - Houston, TX, USA
# - Bajkonur Cosmodrome, Kazachstan
# 2. Użyj `List of tz database time zones` [1]
# 3. Uruchom doctesty - wszystkie muszą się powieść
# %% References
# [1] Wikipedia. List of tz database time zones.
# Retrieved: 2022-12-01
# Year: 2022
# URL: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# [2] IANA. Time Zone Database.
# Retrieved: 2022-12-01
# Year: 2022
# URL: https://data.iana.org/time-zones/releases/
# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> from datetime import datetime
>>> assert cape_canaveral is not Ellipsis, \
'Assign value to variable `cape_canaveral` instead of Ellipsis (...)'
>>> assert houston is not Ellipsis, \
'Assign value to variable `houston` instead of Ellipsis (...)'
>>> assert bajkonur is not Ellipsis, \
'Assign value to variable `bajkonur` instead of Ellipsis (...)'
>>> assert isinstance(cape_canaveral, ZoneInfo), \
'Variable `cape_canaveral` has invalid type, must be a ZoneInfo'
>>> assert isinstance(houston, ZoneInfo), \
'Variable `houston` has invalid type, must be a ZoneInfo'
>>> assert isinstance(bajkonur, ZoneInfo), \
'Variable `bajkonur` has invalid type, must be a ZoneInfo'
>>> def utcoffset(tz: ZoneInfo):
... dt = datetime(2000, 1, 1)
... return tz.utcoffset(dt).total_seconds()
>>>
>>> assert utcoffset(cape_canaveral) == -18000.0, \
'Invalid time zone, check IANA Time Zone Database'
>>> assert utcoffset(houston) == -21600.0, \
'Invalid time zone, check IANA Time Zone Database'
>>> assert utcoffset(bajkonur) == 21600, \
'Invalid time zone, check IANA Time Zone Database'
"""
from zoneinfo import ZoneInfo
# Timezone in Cape Canaveral, FL, USA
# type: ZoneInfo
cape_canaveral = ...
# Timezone in Houston, TX, USA= ...
# type: ZoneInfo
houston = ...
# Timezone in Bajkonur Cosmodrome, Kazachstan
# type: ZoneInfo
bajkonur = ...
# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`
# %% About
# - Name: Datetime Timezone ZoneInfo
# - Difficulty: easy
# - Lines: 2
# - Minutes: 13
# %% English
# 0. If you're on Windows then install module `tzdata` (`pip install tzdata`)
# 1. Create `zoneinfo.ZoneInfo` object of:
# - North Pole
# - South Pole (Henryk Arctowski Polish Antarctic Station)
# 2. Use:
# - `List of tz database time zones` [1]
# - `Time in Antarctica` [3]
# - `Wyspa Króla Jerzego` [4]
# 3. Run doctests - all must succeed
# %% Polish
# 0. Jeżeli masz Windows to zainstaluj moduł `tzdata` (`pip install tzdata`)
# 1. Stwórz obiekt `zoneinfo.ZoneInfo` z:
# - North Pole
# - South Pole (Henryk Arctowski Polish Antarctic Station)
# 2. Użyj:
# - `List of tz database time zones` [1]
# - `Time in Antarctica` [3]
# - `Wyspa Króla Jerzego` [4]
# 3. Uruchom doctesty - wszystkie muszą się powieść
# %% References
# [1] Wikipedia. List of tz database time zones.
# Retrieved: 2022-12-01
# Year: 2022
# URL: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# [2] IANA. Time Zone Database.
# Retrieved: 2022-12-01
# Year: 2022
# URL: https://data.iana.org/time-zones/releases/
# [3] Time in Antarctica.
# Retrieved: 2022-12-01
# Year: 2022
# URL: https://en.wikipedia.org/wiki/Time_in_Antarctica
# [4] Wyspa Króla Jerzego.
# Retrieved: 2024-11-20
# Year: 2024
# URL: https://arctowski.aq/pl/wyspa-krola-jerzego/
# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> from datetime import datetime
>>> assert north_pole is not Ellipsis, \
'Assign value to variable `north_pole` instead of Ellipsis (...)'
>>> assert south_pole is not Ellipsis, \
'Assign value to variable `south_pole` instead of Ellipsis (...)'
>>> assert isinstance(north_pole, ZoneInfo), \
'Variable `north_pole` has invalid type, must be a ZoneInfo'
>>> assert isinstance(south_pole, ZoneInfo), \
'Variable `south_pole` has invalid type, must be a ZoneInfo'
>>> def utcoffset(tz: ZoneInfo):
... dt = datetime(2000, 1, 1)
... return tz.utcoffset(dt).total_seconds()
>>>
>>> assert utcoffset(north_pole) == 3600, \
'Invalid time zone, check IANA Time Zone Database'
>>> assert utcoffset(south_pole) == 10800, \
'Invalid time zone, check IANA Time Zone Database'
"""
from zoneinfo import ZoneInfo
# Timezone in North Pole
# type: ZoneInfo
north_pole = ...
# Timezone in South Pole
# type: ZoneInfo
south_pole = ...