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

  1. Override utcoffset(self, dt) in the subclass returning a timedelta or None
  2. Replace the custom class with zoneinfo.ZoneInfo(region/city), which implements the full tzinfo contract from the IANA database
  3. 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

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


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