{"record":{"id":"d5305f282ed5f50b","repo":"python/cpython","slug":"tzinfo-subclass-must-override-tzname","errorCode":null,"errorMessage":"tzinfo subclass must override tzname()","messagePattern":"tzinfo subclass must override tzname\\(\\)","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"Lib/_pydatetime.py","lineNumber":1317,"sourceCode":"        return (self.__class__, self._getstate())\n\n_date_class = date  # so functions w/ args named \"date\" can get at the class\n\ndate.min = date(1, 1, 1)\ndate.max = date(9999, 12, 31)\ndate.resolution = timedelta(days=1)\n\n\nclass tzinfo:\n    \"\"\"Abstract base class for time zone info objects.\n\n    Subclasses must override the tzname(), utcoffset() and dst() methods.\n    \"\"\"\n    __slots__ = ()\n\n    def tzname(self, dt):\n        \"datetime -> string name of time zone.\"\n        raise NotImplementedError(\"tzinfo subclass must override tzname()\")\n\n    def utcoffset(self, dt):\n        \"datetime -> timedelta, positive for east of UTC, negative for west of UTC\"\n        raise NotImplementedError(\"tzinfo subclass must override utcoffset()\")\n\n    def dst(self, dt):\n        \"\"\"datetime -> DST offset as timedelta, positive for east of UTC.\n\n        Return 0 if DST not in effect.  utcoffset() must include the DST\n        offset.\n        \"\"\"\n        raise NotImplementedError(\"tzinfo subclass must override dst()\")\n\n    def fromutc(self, dt):\n        \"datetime in UTC -> datetime in local time.\"\n\n        if not isinstance(dt, datetime):\n            raise TypeError(\"fromutc() requires a datetime argument\")","sourceCodeStart":1299,"sourceCodeEnd":1335,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pydatetime.py#L1299-L1335","documentation":"Raised by the abstract base tzinfo.tzname() when a timezone subclass does not override it. tzinfo is an interface: tzname(), utcoffset() and dst() must all be implemented by concrete subclasses. This error means someone instantiated/used a tzinfo subclass (including tzinfo itself) that left tzname() unimplemented.","triggerScenarios":"class MyTZ(tzinfo): pass; then MyTZ().tzname(dt) — or any datetime with tz=MyTZ() calling .tzname(), or strftime('%Z') on such a datetime. Also calling tzinfo().tzname(dt) directly on the base class.","commonSituations":"Writing a custom timezone class for legacy regional rules and forgetting one of the three required methods; partially copy-pasted tzinfo subclasses from tutorials; migrating from pytz where method names differ.","solutions":["Override tzname(self, dt) in the subclass to return the zone name string (or None)","Prefer the built-in datetime.timezone or zoneinfo.ZoneInfo instead of a hand-rolled tzinfo subclass","If the method is intentionally unsupported, return None rather than inheriting the abstract stub"],"exampleFix":"// before\nclass MyTZ(tzinfo):\n    def utcoffset(self, dt): return timedelta(hours=5)\n    def dst(self, dt): return timedelta(0)\n\n// after\nclass MyTZ(tzinfo):\n    def utcoffset(self, dt): return timedelta(hours=5)\n    def dst(self, dt): return timedelta(0)\n    def tzname(self, dt): return 'UTC+05'","handlingStrategy":"validation","validationCode":"for m in ('tzname', 'utcoffset', 'dst'):\n    if type(tz).__getattribute__(type(tz), m) is getattr(tzinfo, m):\n        raise TypeError(f'{type(tz).__name__} must implement {m}()')","typeGuard":"def is_complete_tzinfo(tz) -> bool:\n    return all(getattr(type(tz), m, None) is not getattr(tzinfo, m)\n               for m in ('tzname', 'utcoffset', 'dst'))","tryCatchPattern":"try:\n    name = dt.tzname()\nexcept NotImplementedError:\n    name = None  # zone name unavailable; proceed without it","preventionTips":["Prefer zoneinfo.ZoneInfo or datetime.timezone over custom tzinfo subclasses","Add an abstract-method test that instantiates your tzinfo subclass and calls all three methods","Return None instead of inheriting abstract stubs for genuinely unsupported methods"],"tags":["datetime","timezone","notimplementederror","subclassing"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}