Exceptions and warnings

Warnings

exception whenever.NaiveArithmeticWarning[source]

Bases: PotentialDstBugWarning

Raised when exact-time arithmetic is performed on a PlainDateTime without timezone context.

PlainDateTime carries no timezone 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 two PlainDateTime values, the computation treats every hour as equal. If a timezone transition falls in the interval, the result may be off by an hour or more.

When it can occur

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 timezone with assume_tz() first, then perform arithmetic on the resulting ZonedDateTime:

d.assume_tz("Europe/Amsterdam").add(hours=2)
# ZonedDateTime("2023-10-29 02:30:00+01:00[Europe/Amsterdam]")  ✓

To suppress when timezone context doesn’t apply (e.g. simulations, clock times not tied to a real-world timezone, 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.DaysAssumed24HoursWarning[source]

Bases: PotentialDstBugWarning

Raised when days are treated as exactly 24 hours, which may be wrong across a DST transition.

TimeDelta always represents exact time. Constructing one with days or weeks kwargs converts those units to nanoseconds using fixed 86400-second days. If you later add this delta to a ZonedDateTime on 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

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 on a spring-forward day gives the
# wrong local time:
eve = ZonedDateTime(2025, 3, 30, 12, tz="Europe/Amsterdam")
eve + delta
# ZonedDateTime("2025-03-31 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-31 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: PotentialDstBugWarning

Raised when an OffsetDateTime operation may silently preserve an incorrect UTC offset.

A fixed UTC offset (e.g. +02:00) carries no timezone rules — it doesn’t know about DST, historical offset changes, or future policy decisions. After shifting, rounding, or replacing fields of an OffsetDateTime, the original offset is kept verbatim. If the region’s rules changed since that offset was recorded, the result is a timestamp that is off by the difference — silently.

When it can occur

from whenever import OffsetDateTime

# Denver is UTC-7 in winter, UTC-6 in summer.
# On 2024-03-10, clocks spring forward at 2:00 AM.
d = OffsetDateTime(2024, 3, 9, 13, offset=-7)
d.add(hours=24)  # StaleOffsetWarning
# OffsetDateTime("2024-03-10 13:00:00-07:00")
# ^^ -07:00 is wrong; Denver is -06:00 on this date

How to fix it

Convert to ZonedDateTime first so the offset updates automatically with the timezone rules:

d.assume_tz("America/Denver").add(hours=24)
# ZonedDateTime("2024-03-10 14:00:00-06:00[America/Denver]")  ✓

To suppress when the fixed offset is deliberate and known to be correct, pass stale_offset_ok=True (or use Python’s standard warning filters):

d.add(hours=24, stale_offset_ok=True)
exception whenever.PotentialDstBugWarning[source]

Bases: UserWarning

Base class for warnings about potential DST-related bugs in user code.

Not raised directly. Subclasses cover three distinct scenarios:

Catching or filtering this base class handles all three at once:

import warnings, whenever
warnings.filterwarnings("error", category=whenever.PotentialDstBugWarning)
exception whenever.WheneverDeprecationWarning[source]

Bases: UserWarning

Raised when a deprecated feature of the whenever library 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 standard DeprecationWarning, which Python silences in production code.

Exceptions

exception whenever.RepeatedTime[source]

Bases: ValueError

A datetime is repeated in a timezone, e.g. because of DST

exception whenever.SkippedTime[source]

Bases: ValueError

A datetime is skipped in a timezone, e.g. because of DST

exception whenever.InvalidOffsetError[source]

Bases: ValueError

A string has an invalid offset for the given zone

exception whenever.TimeZoneNotFoundError[source]

Bases: ValueError

A timezone with the given ID was not found