Skip to the solution
MediumBackend5 min read

How do you handle dates, times and time zones in Python?

462 views

The problem

Question presented to candidate: "Our service stores timestamps from users all over the world and shows them in each user's local time. How do you work with dates, times and time zones in Python, and what goes wrong around daylight saving time?"

What a strong answer should cover:

  • The datetime module types: date, time, datetime, timedelta, timezone; the difference between a naive datetime (no tzinfo) and an aware one, and that comparing the two raises TypeError
  • Store and compute in UTC (datetime.now(timezone.utc)), convert to a local zone only for display with astimezone(); datetime.utcnow() returns a naive value and is deprecated since Python 3.12
  • Real time zones with zoneinfo.ZoneInfo("Europe/London") (standard library since 3.9, PEP 615) backed by the IANA database (the OS copy or the tzdata package); a fixed timezone(timedelta(hours=-5)) offset is not a time zone because it never changes for DST
  • DST edge cases: the spring-forward gap (02:30 that never happens), the fall-back fold (01:30 twice, chosen with fold=0/1), and that arithmetic within one zone is wall-clock arithmetic, so convert to UTC for elapsed time
  • Serialise with ISO 8601 (isoformat() / fromisoformat(), which accepts a trailing Z since 3.11) or Unix timestamps; strptime/strftime for custom formats; replace(tzinfo=...) attaches a zone while astimezone() converts
  • time.monotonic() / time.perf_counter() for measuring durations, not wall-clock datetimes

Clarifying questions expected:

  • Do we need the user's real time zone (with DST rules) or only a fixed offset?
  • What format do timestamps arrive in, and do they carry an offset?
  • Do we care about calendar arithmetic (add one month, same local time tomorrow) or elapsed time?

Code / implementation expected: Optional โ€” store an aware UTC datetime, convert it to two ZoneInfo zones, and show one DST edge case.

  • datetime
  • zoneinfo

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

5 min read ยท 11 sections

Run the code

Edit the code and run it here. Each console line with an expected value becomes a test.

Pythondatetime, timedelta, fixed offsets, parsing and formatting
Pythonzoneinfo: daylight saving gaps, folds and utcnow (needs the IANA database)
# Real time zones with zoneinfo (needs the IANA database: the OS copy or the tzdata package)
import warnings
from datetime import datetime, timedelta, timezone, UTC
from zoneinfo import ZoneInfo

ny = ZoneInfo("America/New_York")
kolkata = ZoneInfo("Asia/Kolkata")

# One instant, three wall clocks
t = datetime(2024, 7, 1, 12, 0, tzinfo=UTC)
print(t.astimezone(ny), t.astimezone(ny).tzname())
print(t.astimezone(kolkata), t.astimezone(kolkata).tzname())

# Same zone, different offset: daylight saving time
def hours(td):
    return td.total_seconds() / 3600
print("January offset:", hours(datetime(2024, 1, 15, 12, tzinfo=ny).utcoffset()))
print("July offset:   ", hours(datetime(2024, 7, 15, 12, tzinfo=ny).utcoffset()))

# Spring forward: 2024-03-10 02:00 jumps to 03:00 in New York
before = datetime(2024, 3, 9, 12, 0, tzinfo=ny)
wall = before + timedelta(days=1)
print("wall clock +1 day:", wall, "elapsed:", wall.astimezone(UTC) - before.astimezone(UTC))
real = (before.astimezone(UTC) + timedelta(days=1)).astimezone(ny)
print("absolute +24h:   ", real)

# 02:30 on that day never happened on a New York clock
gap = datetime(2024, 3, 10, 2, 30, tzinfo=ny)
print("nonexistent 02:30 round-trips to", gap.astimezone(UTC).astimezone(ny))

# Fall back: 2024-11-03 01:30 happens twice; fold picks which one
first = datetime(2024, 11, 3, 1, 30, tzinfo=ny)
second = first.replace(fold=1)
print(hours(first.utcoffset()), hours(second.utcoffset()), first.tzname(), second.tzname())
print(first.astimezone(UTC), second.astimezone(UTC))

# Subtracting two datetimes in the SAME zone object ignores the DST change
a = datetime(2024, 3, 10, 1, 0, tzinfo=ny)
b = datetime(2024, 3, 10, 4, 0, tzinfo=ny)
print("same tzinfo:", b - a, " via UTC:", b.astimezone(UTC) - a.astimezone(UTC))

# Store UTC, convert for display
stored = datetime(2024, 11, 3, 6, 30, tzinfo=UTC)
for zone in ["America/New_York", "Europe/London", "Asia/Kolkata"]:
    print(f"{zone:18}", stored.astimezone(ZoneInfo(zone)).strftime("%Y-%m-%d %H:%M %Z"))

# Fixed offset vs real zone: a fixed -05:00 never moves for summer
fixed = timezone(timedelta(hours=-5))
print(datetime(2024, 7, 1, 12, tzinfo=fixed).astimezone(UTC), datetime(2024, 7, 1, 12, tzinfo=ny).astimezone(UTC))

# utcnow() returns a NAIVE datetime and is deprecated since 3.12
with warnings.catch_warnings(record=True) as caught:
    warnings.simplefilter("always")
    n = datetime.utcnow()
print(n.tzinfo, caught[0].category.__name__)
print(datetime.now(UTC).tzinfo)

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 90 of 139 in Python

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Python questions
Up nextMedium

What is the __hash__ and __eq__ contract?

Continue