One type for everything¶
Python uses a single datetime type to represent two fundamentally different concepts:
Naive datetimes, which have no time zone information
Aware datetimes, which are associated with a time zone
These two behave differently, are interpreted differently, and should never be mixed. Yet the type system makes no distinction between them.
This becomes especially frustrating in typed code.
There is no way to express, using type annotations,
whether a function expects a naive or an aware datetime.
def schedule_at(dt: datetime) -> None:
...
Does dt represent a local wall-clock time? A UTC timestamp? A zoned time?
The type gives you no way to say.
This makes it impossible to statically enforce one of the most important invariants in date-time code. As a result, mistakes that should be caught early often surface only at runtime—or worse, much later.
How whenever solves this¶
whenever strictly separates datetimes with and without time zone information:
PlainDateTimeis the equivalent of a “naive” timeZonedDateTimeis the equivalent of an “aware” time withZoneInfoattachedInstantis the equivalent of an “aware” time withUTCattached
This makes type annotations precise and self-documenting:
def schedule_at(dt: ZonedDateTime) -> None:
...