python/cpython · error · NotImplementedError

tzinfo subclass must override dst()

Error message

tzinfo subclass must override dst()

What it means

Raised by the abstract base tzinfo.dst() when a timezone subclass does not override it. dst(dt) must return the daylight-saving offset as a timedelta (or zero/None when DST is not in effect); utcoffset() is expected to already include it. Any code path that asks a datetime for its DST state (e.g. the default fromutc algorithm) hits this stub on incomplete subclasses.

Source

Thrown at Lib/_pydatetime.py:1329

    Subclasses must override the tzname(), utcoffset() and dst() methods.
    """
    __slots__ = ()

    def tzname(self, dt):
        "datetime -> string name of time zone."
        raise NotImplementedError("tzinfo subclass must override tzname()")

    def utcoffset(self, dt):
        "datetime -> timedelta, positive for east of UTC, negative for west of UTC"
        raise NotImplementedError("tzinfo subclass must override utcoffset()")

    def dst(self, dt):
        """datetime -> DST offset as timedelta, positive for east of UTC.

        Return 0 if DST not in effect.  utcoffset() must include the DST
        offset.
        """
        raise NotImplementedError("tzinfo subclass must override dst()")

    def fromutc(self, dt):
        "datetime in UTC -> datetime in local time."

        if not isinstance(dt, datetime):
            raise TypeError("fromutc() requires a datetime argument")
        if dt.tzinfo is not self:
            raise ValueError("dt.tzinfo is not self")

        dtoff = dt.utcoffset()
        if dtoff is None:
            raise ValueError("fromutc() requires a non-None utcoffset() "
                             "result")

        # See the long comment block at the end of this file for an
        # explanation of this algorithm.
        dtdst = dt.dst()
        if dtdst is None:

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Override dst(self, dt) to return timedelta(0) for zones without DST
  2. Use datetime.timezone (fixed offset, DST-free by definition) or zoneinfo.ZoneInfo (full DST rules) instead of subclassing
  3. If overriding fromutc() yourself, ensure dst() is still defined for other callers

Example fix

// before
class MyTZ(tzinfo):
    def utcoffset(self, dt): return timedelta(hours=9)
    def tzname(self, dt): return 'JST'

// after
class MyTZ(tzinfo):
    def utcoffset(self, dt): return timedelta(hours=9)
    def tzname(self, dt): return 'JST'
    def dst(self, dt): return timedelta(0)
Defensive patterns

Strategy: validation

Validate before calling

assert type(tz).dst is not tzinfo.dst, 'dst() not overridden'
# also exercise the fromutc path used by astimezone
_ = datetime(2024, 1, 1, tzinfo=tz).astimezone(timezone.utc)

Type guard

def tz_has_dst(tz) -> bool:
    return type(tz).dst is not tzinfo.dst

Try / catch

try:
    _ = dt.dst()
except NotImplementedError:
    pass  # no DST info; skip DST-dependent logic

Prevention

When it happens

Trigger: class MyTZ(tzinfo): pass (or missing only dst) then datetime.now(MyTZ()).dst(); calling .astimezone() on a datetime attached to such a tzinfo, which invokes the default fromutc and its dst() call; direct MyTZ().dst(dt).

Common situations: Fixed-offset custom timezones where the author assumed dst() would never be called — but fromutc() and some formatting paths still invoke it; test doubles for tzinfo that implement only utcoffset.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/5843d78e19fedcfe. Report an issue: GitHub.