python/cpython · error · NotImplementedError
tzinfo subclass must override utcoffset()
Error message
tzinfo subclass must override utcoffset()
What it means
Raised by the abstract base tzinfo.utcoffset() when a timezone subclass does not override it. utcoffset(dt) is the core method of the tzinfo contract: it must return a timedelta (positive east of UTC) or None for a naive datetime. Without it, aware datetime arithmetic, comparison, and UTC conversion cannot function, so the base class raises NotImplementedError.
Source
Thrown at Lib/_pydatetime.py:1321
date.min = date(1, 1, 1)
date.max = date(9999, 12, 31)
date.resolution = timedelta(days=1)
class tzinfo:
"""Abstract base class for time zone info objects.
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()View on GitHub (pinned to bc6749cc3b)
Solutions
- Override utcoffset(self, dt) in the subclass returning a timedelta or None
- Replace the custom class with zoneinfo.ZoneInfo(region/city), which implements the full tzinfo contract from the IANA database
- For fixed offsets use datetime.timezone(timedelta(hours=N)) instead of subclassing
Example fix
// before
class MyTZ(tzinfo):
def tzname(self, dt): return 'X'
// after
from datetime import timezone, timedelta
MyTZ = timezone(timedelta(hours=5), name='X') Defensive patterns
Strategy: validation
Validate before calling
assert tz.utcoffset(datetime.now()) is not NotImplemented # stronger: verify the method is not the abstract stub assert type(tz).utcoffset is not tzinfo.utcoffset
Type guard
def tz_has_utcoffset(tz) -> bool:
return type(tz).utcoffset is not tzinfo.utcoffset Try / catch
try:
off = dt.utcoffset()
except NotImplementedError:
off = None # treat as naive or reject the tzinfo explicitly Prevention
- Use zoneinfo.ZoneInfo for real zones; datetime.timezone for fixed offsets
- Unit-test every custom tzinfo against datetime.now(tz), .utcoffset(), .tzname(), .dst()
- Never ship a tzinfo subclass with inherited abstract methods
When it happens
Trigger: class MyTZ(tzinfo): pass used as datetime.now(MyTZ()).utcoffset(); calling .astimezone(timezone.utc) on a datetime whose tzinfo lacks utcoffset; strftime('%z') on such a datetime; direct MyTZ().utcoffset(dt) calls.
Common situations: Custom tzinfo subclasses for historical or fictional timezones where only tzname was implemented; stub timezone classes created for tests; incomplete port of a timezone implementation from another language.
Related errors
- tzinfo subclass must override tzname()
- tzinfo subclass must override dst()
- fromutc() requires a non-None utcoffset() result
- Malformed time zone string
- tzinfo.tzname() must return None or string, not {type(name).
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/6199115b59cc971a.
Report an issue: GitHub.