python/cpython · error · TypeError

utcoffset() argument must be a datetime instance or None

Error message

utcoffset() argument must be a datetime instance or None

What it means

timezone.utcoffset(dt) only accepts a datetime instance or None (None is what some generic tzinfo machinery passes). Because a fixed-offset zone's answer never depends on dt, the argument is still type-checked to honor the tzinfo protocol. Any other type raises TypeError.

Source

Thrown at Lib/_pydatetime.py:2517

        "datetime.timezone(datetime.timedelta(-1, 68400), 'EST')"
        """
        if self is self.utc:
            return 'datetime.timezone.utc'
        if self._name is None:
            return "%s%s(%r)" % (_get_class_module(self),
                                 self.__class__.__qualname__,
                                 self._offset)
        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):

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Pass a datetime: tz.utcoffset(datetime(2024,1,1)) or None
  2. In generic tzinfo-driven code, only forward datetime or None per the protocol
  3. If you only need the offset of the zone itself, read tz.utcoffset(None)

Example fix

// before
off = tz.utcoffset('2024-01-01')

# after
off = tz.utcoffset(datetime.fromisoformat('2024-01-01'))
Defensive patterns

Strategy: type-guard

Validate before calling

from datetime import datetime

def safe_utcoffset(tz, dt):
    return tz.utcoffset(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.utcoffset('2024-01-01'); tz.utcoffset(date(2024,1,1)); tz.utcoffset(0); calling with a datetime.date instead of datetime. Usually hit via custom schedulers passing generic values into tzinfo methods.

Common situations: Generic code that forwards whatever it holds into utcoffset; confusing datetime.date with datetime.datetime; porting examples that passed datetimes but the local variable was rebound to a string.

Related errors


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