Exceptions and warnings¶
A value outside its domain raises ValueError, on both backends; the four
exceptions below refine it, so one except ValueError catches everything
parsing and conversion can raise. OverflowError escapes only for an
integer too large for the backend’s machine integer. An argument of the wrong
type usually raises TypeError (sometimes AttributeError); that is not
guaranteed, so rely on a type checker to catch it.
Warnings¶
- exception whenever.WheneverWarning[source]¶
Bases:
UserWarningBase class for all warnings emitted by the
wheneverlibrary.This can be used with Python’s standard warning filters to suppress or escalate all warnings emitted by
whenever:import warnings, whenever warnings.filterwarnings("error", category=whenever.WheneverWarning)
- exception whenever.PotentialDstBugWarning[source]¶
Bases:
WheneverWarningBase class for warnings about potential DST-related bugs in user code.
Not raised directly. Subclasses cover four distinct scenarios:
DaysAssumed24HoursWarning— days treated as exact 24-hour unitsStaleOffsetWarning— fixed offset may become stale relative to its source time zoneNaiveArithmeticWarning— exact-time arithmetic without time zone contextImplicitDisambiguationWarning— resolving a repeated or skipped local time without an explicit policy
Catching or filtering this base class handles all four at once:
import warnings, whenever warnings.filterwarnings("error", category=whenever.PotentialDstBugWarning)
- exception whenever.DaysAssumed24HoursWarning[source]¶
Bases:
PotentialDstBugWarningEmitted when days are treated as exactly 24 hours, which may be wrong across a DST transition.
TimeDeltaalways represents exact time. Constructing one withdaysorweekskwargs converts those units to nanoseconds using fixed 86400-second days. If you later add this delta to aZonedDateTimeon a day where clocks spring forward or fall back, the local time of the result will be off by the transition length (usually one hour).When it can occur
Constructing a
TimeDeltawithdaysorweeks, passing them toInstant.add(),Instant.subtract(), orTimePatch.shift(), and the delta methods that read them as exact units, such asTimeDelta.in_units().from whenever import TimeDelta, ZonedDateTime # TimeDelta(days=1) is exactly 86 400 seconds — no DST awareness. delta = TimeDelta(days=1) # DaysAssumed24HoursWarning # Adding it to a ZonedDateTime across a spring-forward night gives # the wrong local time: eve = ZonedDateTime(2025, 3, 29, 12, tz="Europe/Amsterdam") eve + delta # ZonedDateTime("2025-03-30 13:00:00+02:00[Europe/Amsterdam]") # ^^ 13:00, not 12:00 — one hour lost to the DST transition
How to fix it
Use calendar-based arithmetic directly on the datetime to preserve local time across transitions:
eve.add(days=1) # ZonedDateTime("2025-03-30 12:00:00+02:00[Europe/Amsterdam]") ✓
To suppress when exact 24-hour arithmetic is genuinely intended, pass
days_assumed_24h_ok=True(or use Python’s standard warning filters):TimeDelta(days=1, days_assumed_24h_ok=True)
- exception whenever.StaleOffsetWarning[source]¶
Bases:
PotentialDstBugWarningEmitted when an
OffsetDateTimeoperation may preserve an offset that is stale relative to its source time zone.Pass
stale_offset_ok=Trueto accept this for one call. See OffsetDateTime for the stale-offset footgun, examples, and remediation.
- exception whenever.NaiveArithmeticWarning[source]¶
Bases:
PotentialDstBugWarningEmitted when exact-time arithmetic is performed on a
PlainDateTimewithout time zone context.PlainDateTimecarries no time zone information, so it can’t account for DST transitions. When you add or subtract exact time units (hours, minutes, seconds) or measure the exact difference between twoPlainDateTimevalues, the computation treats every hour as equal. If a time zone transition falls in the interval, the result may be off by an hour or more.When it can occur
Adding or subtracting exact units with
add(),subtract(),+, or-; measuring exact units with-,difference(),since(), oruntil(); and a delta method whoserelative_tois aPlainDateTime.from whenever import PlainDateTime # On 2023-10-29, Amsterdam clocks fall back at 3:00 AM. # PlainDateTime has no knowledge of this. d = PlainDateTime(2023, 10, 29, 1, 30) d.add(hours=2) # NaiveArithmeticWarning # PlainDateTime("2023-10-29 03:30:00") # ^^ only 1 real hour passed in Amsterdam (clocks went back) # Also emitted for exact-unit differences: d2 = PlainDateTime(2023, 10, 30, 1, 30) d2 - d # NaiveArithmeticWarning
How to fix it
Attach a time zone with
assume_tz()first, then perform arithmetic on the resultingZonedDateTime:d.assume_tz("Europe/Amsterdam").add(hours=2) # ZonedDateTime("2023-10-29 02:30:00+01:00[Europe/Amsterdam]") ✓
To suppress when time zone context doesn’t apply (e.g. simulations, clock times not tied to a real-world time zone, or when you know no transitions occur in the interval), pass
naive_arithmetic_ok=True(or use Python’s standard warning filters):d.add(hours=2, naive_arithmetic_ok=True)
- exception whenever.ImplicitDisambiguationWarning[source]¶
Bases:
PotentialDstBugWarningEmitted when a repeated or skipped local datetime is resolved without an explicit disambiguation policy.
Such local datetimes occur around time zone transitions and do not identify one unambiguous instant. Pass
disambiguation=explicitly to document whether the compatible, earlier, later, or rejecting behavior is intended.
- exception whenever.MonthCompositionWarning¶
Bases:
WheneverWarningWarn when itemized deltas with years or months are composed component-wise.
ItemizedDateDelta(months=1) + ItemizedDateDelta(months=1)isItemizedDateDelta(months=2): the components add up, and the order the deltas would apply in is gone. With years or months that order matters, because they clamp at the end of the month. From January 31, one month and then another lands on March 28; two months at once land on March 31. Days, weeks, and exact units never clamp, and composing them doesn’t warn.Composition is flagged rather than refused: see Footguns are flagged, not forbidden. To apply the deltas in turn, add each to the date or datetime. If the composed delta is what you want, pass
month_composition_ok=Truetoadd()orsubtract().
- exception whenever.PickleOffsetMismatchWarning[source]¶
Bases:
WheneverWarningThe offset stored in a ZonedDateTime pickle no longer matches the time zone’s current rules. The instant is preserved; the local fields and offset are recomputed from the current rules. Not escapable per call: filter the category to reject a mismatch. See the pickling guide and ADR 0006.
- exception whenever.WheneverDeprecationWarning[source]¶
Bases:
WheneverWarningEmitted when a deprecated feature of the
wheneverlibrary is used.This is a custom warning class (not a subclass of
DeprecationWarning) so that deprecation warnings from this library are visible by default—unlike standardDeprecationWarning, which Python silences in application code.
Exceptions¶
- exception whenever.RepeatedTime[source]¶
Bases:
ValueErrorA local time that is repeated in a time zone, e.g. because of DST.
See repeated local time.
- exception whenever.SkippedTime[source]¶
Bases:
ValueErrorA local time that is skipped in a time zone, e.g. because of DST.
See skipped local time.
- exception whenever.InvalidOffsetError[source]¶
Bases:
ValueErrorThe offset in the input matches no offset the time zone applies to the written local time. Raised by the ISO and pattern parsers, the
datetimeconstructor overload, andassume_tz()underoffset_mismatch="raise".
- exception whenever.TimeZoneNotFoundError[source]¶
Bases:
ValueErrorThe string names no time zone: an unknown or malformed time zone ID, or a system time zone that cannot be resolved.