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
- Override dst(self, dt) to return timedelta(0) for zones without DST
- Use datetime.timezone (fixed offset, DST-free by definition) or zoneinfo.ZoneInfo (full DST rules) instead of subclassing
- 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
- Implement dst() returning timedelta(0) for DST-free custom zones
- Remember astimezone()/fromutc() call dst() even for fixed-offset zones
- Cover the astimezone round-trip in tests of any custom tzinfo
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
- tzinfo subclass must override tzname()
- tzinfo subclass must override utcoffset()
- fromutc() requires a non-None dst() result
- fromutc(): dt.dst gave inconsistent results; cannot convert
- Malformed time zone string
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/5843d78e19fedcfe.
Report an issue: GitHub.