Avoid assuming every local wall time exists
Also published in our Blogger archive.
Avoid assuming every local wall time exists
A local date and time can be impossible. In Los Angeles on 2020-03-08, clocks advanced from 01:59:59 to 03:00:00, so the wall time 02:30 never occurred. Simply attaching ZoneInfo("America/Los_Angeles") to that naive value creates an aware object, but that construction by itself does not prove the wall time maps to a real instant.
This example tests existence by trying both possible fold values, converting each candidate to UTC, then converting it back to the same zone. A real local time round-trips to the original naive wall fields with the same fold. Neither interpretation of the gap value does, so the function returns False. The nearby 03:30 value survives a round trip and returns True. The assertions state those two concrete outcomes.
This is a validation technique for a named IANA zone, not a replacement for a scheduling policy. An application still needs to decide whether to reject a gap, ask the user for another time, or move an appointment according to an explicit rule. Repeated times during an autumn transition are valid but ambiguous, so callers should preserve or request the selected fold when they need one unique instant. zoneinfo is available from Python 3.9 and uses system IANA data or the documented fallback data source. Official zoneinfo documentation and the aware datetime documentation provide the underlying timezone model.
AI-assistance disclosure: AI helped draft this educational example; the assertions validate only the shown zone and dates.
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
def local_time_exists(wall_time, zone):
for fold in (0, 1):
candidate = wall_time.replace(tzinfo=zone, fold=fold)
returned = candidate.astimezone(timezone.utc).astimezone(zone)
if returned.replace(tzinfo=None) == wall_time and returned.fold == fold:
return True
return False
los_angeles = ZoneInfo("America/Los_Angeles")
gap = datetime(2020, 3, 8, 2, 30)
after_gap = datetime(2020, 3, 8, 3, 30)
assert local_time_exists(gap, los_angeles) is False
assert local_time_exists(after_gap, los_angeles) is True
print(f"{gap.isoformat()}: does not exist")
print(f"{after_gap.isoformat()}: exists")
2020-03-08T02:30:00: does not exist
2020-03-08T03:30:00: exists