RustPython/RustPython · error · NotImplementedError

tzinfo subclass must override tzname()

Error message

tzinfo subclass must override tzname()

What it means

tzinfo is an abstract base class: its tzname(), utcoffset() and dst() methods exist only to be overridden, and the base implementations raise NotImplementedError. This error means a bare tzinfo instance (or an incomplete subclass) was attached to a time/datetime and something asked it for a time-zone name via tzname(), directly or through strftime('%Z'). Use a concrete implementation such as datetime.timezone, zoneinfo.ZoneInfo, or a complete custom subclass.

Source

Thrown at Lib/_pydatetime.py:1301

        return (self.__class__, self._getstate())

_date_class = date  # so functions w/ args named "date" can get at the class

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 classes.

    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")

View on GitHub (pinned to aaeab4f754)

Solutions

  1. Replace the bare tzinfo with a concrete zone: timezone.utc, timezone(timedelta(hours=n)), or zoneinfo.ZoneInfo('Region/City').
  2. If it is your own subclass, implement tzname(self, dt) returning the display string (e.g. 'EST' or '+05:00').
  3. For naive datetimes pass tzinfo=None instead of a dummy tzinfo.
  4. Audit custom tzinfo classes to confirm all three of tzname/utcoffset/dst are overridden.

Example fix

// before
from datetime import time, tzinfo
t = time(9, 30, tzinfo=tzinfo())  # later t.tzname() -> NotImplementedError

// after
from datetime import time, timedelta, timezone
t = time(9, 30, tzinfo=timezone(timedelta(hours=5)))
print(t.tzname())  # 'UTC+05:00'
Defensive patterns

Strategy: type-guard

Type guard

from datetime import tzinfo

def has_concrete_tzname(tz) -> bool:
    if tz is None:
        return True
    if not isinstance(tz, tzinfo):
        return False
    try:
        tz.tzname(None)
    except NotImplementedError:
        return False
    return True

Try / catch

try:
    name = dt.tzname()
except NotImplementedError:
    name = None  # or reattach timezone.utc / ZoneInfo before formatting

Prevention

When it happens

Trigger: time(12, 0, tzinfo=tzinfo()).tzname(); datetime(..., tzinfo=tzinfo()).strftime('%Z'); a custom tzinfo subclass that overrides utcoffset() and dst() but not tzname(); a stub/mock tz left attached in place of the real zone.

Common situations: Placeholder tzinfo used while wiring up configuration; partial ports from pytz where the old class only implemented offsets; test doubles subclassing tzinfo with just enough to construct; tutorial code copied with tzinfo=tzinfo().

Related errors


AI-assisted analysis of RustPython/RustPython@aaeab4f754 (2026-08-17). Data as JSON: /api/errors/f4bfe315c6c0990d. Report an issue: GitHub.