Exercise 2 solution from datetime import datetime, timezone from zoneinfo import ZoneInfo def hours_between(start, end): naive = (end - start).total_seconds() / 3600 real = (end.astimezone(timezone.utc) - start.astimezone(timezone.utc)).total_seconds() / 3600 return naive, real ny = ZoneInfo("America/New_York") lon = ZoneInfo("Europe/London") shifts = [ ("New York, 8 Mar", datetime(2026, 3, 8, 0, 0, tzinfo=ny), datetime(2026, 3, 8, 8, 0, tzinfo=ny)), ("New York, 1 Nov", datetime(2026, 11, 1, 0, 0, tzinfo=ny), datetime(2026, 11, 1, 8, 0, tzinfo=ny)), ("London, 29 Mar", datetime(2026, 3, 29, 0, 0, tzinfo=lon), datetime(2026, 3, 29, 8, 0, tzinfo=lon)), ("London, 25 Oct", datetime(2026, 10, 25, 0, 0, tzinfo=lon), datetime(2026, 10, 25, 8, 0, tzinfo=lon)), ("New York, 15 Jul", datetime(2026, 7, 15, 0, 0, tzinfo=ny), datetime(2026, 7, 15, 8, 0, tzinfo=ny)), ] for label, a, b in shifts: naive, real = hours_between(a, b) print(f"{label:<17} wall-clock {naive:.1f} h real {real:.1f} h") Output (run, and exactly as shown): New York, 8 Mar wall-clock 8.0 h real 7.0 h New York, 1 Nov wall-clock 8.0 h real 9.0 h London, 29 Mar wall-clock 8.0 h real 7.0 h London, 25 Oct wall-clock 8.0 h real 9.0 h New York, 15 Jul wall-clock 8.0 h real 8.0 h Explanation: when both datetimes carry the SAME ZoneInfo object, Python subtracts their wall-clock readings and ignores the offsets, so the answer is always 8.0. Converting both to UTC first gives the elapsed time that really passed: 7 hours when an hour is skipped, 9 when an hour is repeated, 8 on an ordinary day. A shift paid by the hour needs the second figure.