python/cpython · error · TypeError
tzinfo.tzname() must return None or string, not {type(name).
Error message
tzinfo.tzname() must return None or string, not {type(name).__name__!r} What it means
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.
Source
Thrown at Lib/_pydatetime.py:547
raise ValueError(f"Invalid week: {week}")
if not 0 < day < 8:
raise ValueError(f"Invalid weekday: {day} (range is [1, 7])")
# Now compute the offset from (Y, 1, 1) in days:
day_offset = (week - 1) * 7 + (day - 1)
# Calculate the ordinal day for monday, week 1
day_1 = _isoweek1monday(year)
ord_day = day_1 + day_offset
return _ord2ymd(ord_day)
# Just raise TypeError if the arg isn't None or a string.
def _check_tzname(name):
if name is not None and not isinstance(name, str):
raise TypeError("tzinfo.tzname() must return None or string, "
f"not {type(name).__name__!r}")
# name is the offset-producing method, "utcoffset" or "dst".
# offset is what it returned.
# If offset isn't None or timedelta, raises TypeError.
# If offset is None, returns None.
# Else offset is checked for being in range.
# If it is, its integer value is returned. Else ValueError is raised.
def _check_utc_offset(name, offset):
assert name in ("utcoffset", "dst")
if offset is None:
return
if not isinstance(offset, timedelta):
raise TypeError(f"tzinfo.{name}() must return None "
f"or timedelta, not {type(offset).__name__!r}")
if not -timedelta(1) < offset < timedelta(1):
raise ValueError("offset must be a timedelta "
"strictly between -timedelta(hours=24) and "View on GitHub (pinned to bc6749cc3b)
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
Example fix
// before
class MyTZ(tzinfo):
def tzname(self, dt):
return -5 # int -> TypeError
// after
class MyTZ(tzinfo):
def tzname(self, dt):
return 'UTC-05:00' Defensive patterns
Strategy: type-guard
Validate before calling
name = tz.tzname(dt) assert name is None or isinstance(name, str), 'tzname() must return None or str'
Type guard
def valid_tzname(v) -> bool:
return v is None or isinstance(v, str) Prevention
- 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
When it happens
Trigger: 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().
Common situations: 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.
Related errors
- tzinfo.{name}() must return None or timedelta, not {type(off
- tzinfo argument must be None or of a tzinfo subclass, not {t
- offset must be a timedelta strictly between -timedelta(hours
- fromutc() requires a datetime argument
- Malformed time zone string
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/4dd2aafa2a83e656.
Report an issue: GitHub.