python/cpython · error · TypeError

tzinfo argument must be None or of a tzinfo subclass, not {t

Error message

tzinfo argument must be None or of a tzinfo subclass, not {type(tz).__name__!r}

What it means

Raised by _check_tzinfo_arg() when the tz argument to datetime()/time()/their constructors is neither None nor an instance of datetime.tzinfo. Datetime deliberately accepts only tzinfo subclasses (including timezone and zoneinfo.ZoneInfo); passing a string zone name or a module is rejected.

Source

Thrown at Lib/_pydatetime.py:600

    hour = _index(hour)
    minute = _index(minute)
    second = _index(second)
    microsecond = _index(microsecond)
    if not 0 <= hour <= 23:
        raise ValueError(f"hour must be in 0..23, not {hour}")
    if not 0 <= minute <= 59:
        raise ValueError(f"minute must be in 0..59, not {minute}")
    if not 0 <= second <= 59:
        raise ValueError(f"second must be in 0..59, not {second}")
    if not 0 <= microsecond <= 999999:
        raise ValueError(f"microsecond must be in 0..999999, not {microsecond}")
    if fold not in (0, 1):
        raise ValueError(f"fold must be either 0 or 1, not {fold}")
    return hour, minute, second, microsecond, fold

def _check_tzinfo_arg(tz):
    if tz is not None and not isinstance(tz, tzinfo):
        raise TypeError(
            "tzinfo argument must be None or of a tzinfo subclass, "
            f"not {type(tz).__name__!r}"
        )

def _divide_and_round(a, b):
    """divide a by b and round result to the nearest integer

    When the ratio is exactly half-way between two integers,
    the even integer is returned.
    """
    # Based on the reference implementation for divmod_near
    # in Objects/longobject.c.
    q, r = divmod(a, b)
    # round up if either r / b > 0.5, or r / b == 0.5 and q is odd.
    # The expression r / b > 0.5 is equivalent to 2 * r > b if b is
    # positive, 2 * r < b if b negative.
    r *= 2
    greater_than_half = r > b if b > 0 else r < b

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Use timezone.utc or ZoneInfo('America/New_York') instead of a string
  2. Assign after construction: dt = dt.replace(tzinfo=ZoneInfo(name))
  3. Wrap user-supplied zone strings: tzinfo=ZoneInfo(tz) with a try/except ZoneInfoNotFoundError

Example fix

// before
dt = datetime(2024, 1, 1, tzinfo='UTC')  # TypeError
// after
from datetime import timezone
dt = datetime(2024, 1, 1, tzinfo=timezone.utc)
Defensive patterns

Strategy: type-guard

Validate before calling

from datetime import tzinfo

if tz is not None and not isinstance(tz, tzinfo):
    from zoneinfo import ZoneInfo
    tz = ZoneInfo(str(tz))  # accept IANA name strings explicitly

Type guard

from datetime import tzinfo

def valid_tzarg(tz) -> bool:
    return tz is None or isinstance(tz, tzinfo)

Prevention

When it happens

Trigger: datetime(2024, 1, 1, tzinfo='UTC'); dt.astimezone('Europe/Paris'); passing a pytz timezone object works (it subclasses tzinfo) but passing the string 'UTC' or the zoneinfo module itself does not.

Common situations: Expecting datetime() to accept IANA names like datetime(..., tz='America/New_York'); passing tz='UTC' copied from other frameworks (e.g. pandas accepts 'UTC'); migration from libraries that take string zones.

Related errors


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