Real time zones with zoneinfo

datetime · Practical · 10 min

Any user-facing time, schedule or report must follow real time zone rules, including DST changes.

Real places don't have fixed offsets: Berlin is UTC+1 in winter and UTC+2 in summer, and the rules change by law. The zoneinfo module (3.9+) provides ZoneInfo, a tzinfo backed by the IANA time zone database:

from zoneinfo import ZoneInfo
berlin = ZoneInfo("Europe/Berlin")

Use IANA names ("America/New_York", "Asia/Kolkata"), never abbreviations like "EST" or "CET", which are ambiguous and ignore DST.

  • datetime(2025, 7, 1, 9, tzinfo=berlin) means 9:00 local time in Berlin on that date; its utcoffset() is +2:00 in July and +1:00 in January.
  • dt.astimezone(ZoneInfo("Asia/Tokyo")) shows the same instant in another zone.
  • zoneinfo.available_timezones() lists all known names.

Where the data comes from: on Linux and macOS, the system's tz database; on Windows (or slim containers) there is none, so install the tzdata package from PyPI, which zoneinfo uses automatically as a fallback. Missing data raises ZoneInfoNotFoundError.

ZoneInfo objects are cached: ZoneInfo("UTC") is ZoneInfo("UTC"). Prefer the stdlib zoneinfo over the older third-party pytz, whose localize() API is easy to misuse.

Example

from datetime import UTC, datetime
from zoneinfo import ZoneInfo

berlin = ZoneInfo("Europe/Berlin")
winter = datetime(2025, 1, 15, 9, 0, tzinfo=berlin)
summer = datetime(2025, 7, 15, 9, 0, tzinfo=berlin)
print(winter.utcoffset(), summer.utcoffset())   # 1:00:00 2:00:00
print(summer.tzname())                          # CEST

meeting = datetime(2025, 7, 15, 15, 0, tzinfo=UTC)
for zone in ["America/New_York", "Asia/Kolkata", "Australia/Sydney"]:
    local = meeting.astimezone(ZoneInfo(zone))
    print(f"{zone:18} {local:%a %H:%M %Z}")
# America/New_York   Tue 11:00 EDT
# Asia/Kolkata       Tue 20:30 IST
# Australia/Sydney   Wed 01:00 AEST
print(ZoneInfo("Europe/Berlin") is berlin)      # True

Remember

  • ZoneInfo('Area/City') gives real zones with DST rules
  • Use IANA names, never abbreviations like EST
  • Install tzdata where the OS has no time zone database (Windows, slim images)

Watch out for

  • Using a fixed timezone(timedelta(hours=1)) for a real place that observes DST.

Check yourself

  1. Why is ZoneInfo('Europe/London') better than timezone(timedelta(0)) for London times?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/zoneinfo.html