python/cpython · error · TypeError

tzname() argument must be a datetime instance or None

Error message

tzname() argument must be a datetime instance or None

What it means

timezone.tzname(dt) returns the configured display name (or derives one like 'UTC+05:30' from the offset when no name was set), but only when dt is a datetime instance or None. Other argument types violate the tzinfo method protocol and raise TypeError.

Source

Thrown at Lib/_pydatetime.py:2525

        return "%s%s(%r, %r)" % (_get_class_module(self),
                                 self.__class__.__qualname__,
                                 self._offset, self._name)

    def __str__(self):
        return self.tzname(None)

    def utcoffset(self, dt):
        if isinstance(dt, datetime) or dt is None:
            return self._offset
        raise TypeError("utcoffset() argument must be a datetime instance"
                        " or None")

    def tzname(self, dt):
        if isinstance(dt, datetime) or dt is None:
            if self._name is None:
                return self._name_from_offset(self._offset)
            return self._name
        raise TypeError("tzname() argument must be a datetime instance"
                        " or None")

    def dst(self, dt):
        if isinstance(dt, datetime) or dt is None:
            return None
        raise TypeError("dst() argument must be a datetime instance"
                        " or None")

    def fromutc(self, dt):
        if isinstance(dt, datetime):
            if dt.tzinfo is not self:
                raise ValueError("fromutc: dt.tzinfo "
                                 "is not self")
            return dt + self._offset
        raise TypeError("fromutc() argument must be a datetime instance"
                        " or None")

    _maxoffset = timedelta(hours=24, microseconds=-1)

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Pass a datetime instance or None: tz.tzname(None) is sufficient for fixed zones
  2. Type-check before forwarding in generic wrappers
  3. If you already have an aware datetime dt, prefer dt.tzname() which routes correctly

Example fix

// before
name = tz.tzname('2024-01-01')

# after
name = tz.tzname(None)  # fixed zones ignore dt
Defensive patterns

Strategy: type-guard

Validate before calling

from datetime import datetime

def safe_tzname(tz, dt=None):
    return tz.tzname(dt if isinstance(dt, datetime) or dt is None else None)

Type guard

from datetime import datetime

def is_datetime_or_none(v) -> bool:
    return v is None or isinstance(v, datetime)

Prevention

When it happens

Trigger: tz.tzname(date(2024,1,1)); tz.tzname('now'); tz.tzname(int) — typically from generic formatting code or a mistaken belief any hashable key is accepted.

Common situations: Report generators calling tzname with date objects; refactors where a datetime variable became a string; third-party tzinfo consumers that forward unvalidated input.

Related errors


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