Design philosophy¶
This page describes the guiding principles behind whenever’s API.
For concrete questions, see the FAQ.
Separate types for separate meanings¶
If two concepts carry different semantics,
they get different types—even when they look similar on the surface.
For example, a datetime with a timezone (ZonedDateTime)
and one with a fixed offset (OffsetDateTime) both
represent a moment in time with a local clock reading,
but only the former can track DST transitions.
Encoding this distinction in the type system makes bugs that would
otherwise surface at runtime visible at development time.
This principle also extends to deltas:
an exact duration (TimeDelta),
a bag of calendar units (ItemizedDateDelta),
and a mixed bag (ItemizedDelta) each have
different arithmetic rules.
Keeping them as separate types prevents mixing operations
that don’t make sense together.
Footguns are flagged, not forbidden¶
Some operations are potential footguns—but not always wrong.
For example, doing arithmetic on a PlainDateTime can’t
account for DST, but may be acceptable if the user knows
DST isn’t relevant for their use case, or accepts the possibility
of an incorrect result some of the time.
Outright forbidding these operations would push users toward workarounds
that would obscure their intention. Whenever allows them but emits a
warning,
which can then explicitly and selectively be silenced.
Operators only where mathematically intuitive¶
Operators like +, -, *, and / are only defined
when they obey the mathematical properties you’d expect—associativity,
reversibility, and so on.
For instance, a + (b + c) == (a + b) + c doesn’t hold
when b or c involve months (because months have variable lengths).
Instead of silently breaking those expectations,
whenever omits the operator and provides explicit methods
(add(), subtract())
that don’t carry the same mathematical connotations.
Similarly, the - operator between two datetimes
always returns a TimeDelta (an exact duration),
because that’s the only type where subtraction is always reversible.
For calendar-unit differences, use since()
/ until().
No system timezone by default¶
Many datetime libraries silently use the system timezone as a default,
but this couples your code to the machine’s configuration—a
common source of surprises, especially in servers and containers
where the system timezone is often UTC or undefined.
In whenever, the system timezone is never used implicitly;
you must opt in with a dedicated method
(e.g. to_system_tz(),
assume_system_tz())
so the dependency is visible in the code.