{"record":{"id":"4dd2aafa2a83e656","repo":"python/cpython","slug":"tzinfo-tzname-must-return-none-or-string-not-t","errorCode":null,"errorMessage":"tzinfo.tzname() must return None or string, not {type(name).__name__!r}","messagePattern":"tzinfo\\.tzname\\(\\) must return None or string, not (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"Lib/_pydatetime.py","lineNumber":547,"sourceCode":"            raise ValueError(f\"Invalid week: {week}\")\n\n    if not 0 < day < 8:\n        raise ValueError(f\"Invalid weekday: {day} (range is [1, 7])\")\n\n    # Now compute the offset from (Y, 1, 1) in days:\n    day_offset = (week - 1) * 7 + (day - 1)\n\n    # Calculate the ordinal day for monday, week 1\n    day_1 = _isoweek1monday(year)\n    ord_day = day_1 + day_offset\n\n    return _ord2ymd(ord_day)\n\n\n# Just raise TypeError if the arg isn't None or a string.\ndef _check_tzname(name):\n    if name is not None and not isinstance(name, str):\n        raise TypeError(\"tzinfo.tzname() must return None or string, \"\n                        f\"not {type(name).__name__!r}\")\n\n# name is the offset-producing method, \"utcoffset\" or \"dst\".\n# offset is what it returned.\n# If offset isn't None or timedelta, raises TypeError.\n# If offset is None, returns None.\n# Else offset is checked for being in range.\n# If it is, its integer value is returned.  Else ValueError is raised.\ndef _check_utc_offset(name, offset):\n    assert name in (\"utcoffset\", \"dst\")\n    if offset is None:\n        return\n    if not isinstance(offset, timedelta):\n        raise TypeError(f\"tzinfo.{name}() must return None \"\n                        f\"or timedelta, not {type(offset).__name__!r}\")\n    if not -timedelta(1) < offset < timedelta(1):\n        raise ValueError(\"offset must be a timedelta \"\n                         \"strictly between -timedelta(hours=24) and \"","sourceCodeStart":529,"sourceCodeEnd":565,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pydatetime.py#L529-L565","documentation":"Raised by datetime's internal _check_tzname() when a custom tzinfo subclass's tzname(dt) method returns a value that is neither None nor a str. The datetime module calls tzname() internally (e.g. when formatting aware datetimes or computing tzname()) and enforces the return-type contract of the tzinfo ABC. Any non-string truthy value (int, bytes, tuple) triggers this TypeError.","triggerScenarios":"Calling dt.strftime('%Z') or datetime.tzname() on an aware datetime whose tzinfo subclass returns e.g. an int offset or bytes from tzname(); also astimezone()/isoformat() paths that consult tzname().","commonSituations":"Custom timezone wrappers that return an offset code (e.g. -5) instead of 'UTC-05:00'; returning bytes from a tzname implementation ported from Python 2; stub tzinfo classes in tests that return mock objects.","solutions":["Make the custom tzinfo.tzname(dt) return a str (e.g. 'EST') or None","If a non-string identifier is needed internally, keep it in a separate attribute/method and only return its str() form from tzname()","Check every tzinfo hook (utcoffset, dst, tzname) against the ABC contract in the docs"],"exampleFix":"// before\nclass MyTZ(tzinfo):\n    def tzname(self, dt):\n        return -5  # int -> TypeError\n// after\nclass MyTZ(tzinfo):\n    def tzname(self, dt):\n        return 'UTC-05:00'","handlingStrategy":"type-guard","validationCode":"name = tz.tzname(dt)\nassert name is None or isinstance(name, str), 'tzname() must return None or str'","typeGuard":"def valid_tzname(v) -> bool:\n    return v is None or isinstance(v, str)","tryCatchPattern":null,"preventionTips":["Unit-test custom tzinfo subclasses: assert tzname(dt) is None or isinstance(tzname(dt), str)","Follow the tzinfo ABC contract exactly: utcoffset/dst -> timedelta|None, tzname -> str|None"],"tags":["datetime","tzinfo","typeerror","timezone"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}