{"record":{"id":"544aa4dff19e0eb4","repo":"python/cpython","slug":"tz-argument-must-be-an-instance-of-tzinfo","errorCode":null,"errorMessage":"tz argument must be an instance of tzinfo","messagePattern":"tz argument must be an instance of tzinfo","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"Lib/_pydatetime.py","lineNumber":2131,"sourceCode":"            ts = self._mktime()\n            # Detect gap\n            ts2 = self.replace(fold=1-self.fold)._mktime()\n            if ts2 != ts: # This happens in a gap or a fold\n                if (ts2 > ts) == self.fold:\n                    ts = ts2\n        else:\n            ts = (self - _EPOCH) // timedelta(seconds=1)\n        localtm = _time.localtime(ts)\n        # Extract TZ data\n        gmtoff = localtm.tm_gmtoff\n        zone = localtm.tm_zone\n        return timezone(timedelta(seconds=gmtoff), zone)\n\n    def astimezone(self, tz=None):\n        if tz is None:\n            tz = self._local_timezone()\n        elif not isinstance(tz, tzinfo):\n            raise TypeError(\"tz argument must be an instance of tzinfo\")\n\n        mytz = self.tzinfo\n        if mytz is None:\n            mytz = self._local_timezone()\n            myoffset = mytz.utcoffset(self)\n        else:\n            myoffset = mytz.utcoffset(self)\n            if myoffset is None:\n                mytz = self.replace(tzinfo=None)._local_timezone()\n                myoffset = mytz.utcoffset(self)\n\n        if tz is mytz:\n            return self\n\n        # Convert self to UTC, and attach the new time zone object.\n        utc = (self - myoffset).replace(tzinfo=tz)\n\n        # Convert from UTC to tz's local time.","sourceCodeStart":2113,"sourceCodeEnd":2149,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pydatetime.py#L2113-L2149","documentation":"datetime.astimezone(tz) requires tz to be a tzinfo instance (or None, which selects the local timezone). Any other value — string zone name, int offset, ZoneInfo-less code path — triggers this TypeError before any conversion happens.","triggerScenarios":"dt.astimezone('UTC'), dt.astimezone(0), or dt.astimezone(timezone.utc) where timezone was shadowed/not imported. Passing pytz timezone objects works (they are tzinfo subclasses), but raw strings never do.","commonSituations":"Config files storing TZ as a string ('US/Eastern') passed directly; forgetting to wrap offsets: astimezone(timedelta(hours=2)) instead of astimezone(timezone(timedelta(hours=2))); mixing up ZoneInfo('UTC') with the literal 'UTC'.","solutions":["Convert string names: dt.astimezone(ZoneInfo('US/Eastern'))","Wrap fixed offsets: dt.astimezone(timezone(timedelta(hours=2)))","For UTC use dt.astimezone(timezone.utc)","Check for shadowed imports of timezone/ZoneInfo in the module"],"exampleFix":"// before\ndt = dt.astimezone('UTC')\n\n# after\nfrom zoneinfo import ZoneInfo\ndt = dt.astimezone(ZoneInfo('UTC'))","handlingStrategy":"type-guard","validationCode":"from datetime import tzinfo\n\ndef as_tzinfo(tz):\n    if tz is None or isinstance(tz, tzinfo):\n        return tz\n    if isinstance(tz, str):\n        from zoneinfo import ZoneInfo\n        return ZoneInfo(tz)\n    raise TypeError(f'cannot interpret {tz!r} as a timezone')","typeGuard":"from datetime import tzinfo\n\ndef is_tzinfo(v) -> bool:\n    return v is None or isinstance(v, tzinfo)","tryCatchPattern":null,"preventionTips":["Store zone names in config but convert via ZoneInfo at load","Standardize on timezone.utc for UTC","Never pass raw offsets — wrap in timezone(timedelta(...))"],"tags":["python","datetime","timezone","type-error"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}