{"record":{"id":"d83d0a66455e2938","repo":"python/cpython","slug":"dt-tzinfo-is-not-self","errorCode":null,"errorMessage":"dt.tzinfo is not self","messagePattern":"dt\\.tzinfo is not self","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Lib/_pydatetime.py","lineNumber":1337,"sourceCode":"    def utcoffset(self, dt):\n        \"datetime -> timedelta, positive for east of UTC, negative for west of UTC\"\n        raise NotImplementedError(\"tzinfo subclass must override utcoffset()\")\n\n    def dst(self, dt):\n        \"\"\"datetime -> DST offset as timedelta, positive for east of UTC.\n\n        Return 0 if DST not in effect.  utcoffset() must include the DST\n        offset.\n        \"\"\"\n        raise NotImplementedError(\"tzinfo subclass must override dst()\")\n\n    def fromutc(self, dt):\n        \"datetime in UTC -> datetime in local time.\"\n\n        if not isinstance(dt, datetime):\n            raise TypeError(\"fromutc() requires a datetime argument\")\n        if dt.tzinfo is not self:\n            raise ValueError(\"dt.tzinfo is not self\")\n\n        dtoff = dt.utcoffset()\n        if dtoff is None:\n            raise ValueError(\"fromutc() requires a non-None utcoffset() \"\n                             \"result\")\n\n        # See the long comment block at the end of this file for an\n        # explanation of this algorithm.\n        dtdst = dt.dst()\n        if dtdst is None:\n            raise ValueError(\"fromutc() requires a non-None dst() result\")\n        delta = dtoff - dtdst\n        if delta:\n            dt += delta\n            dtdst = dt.dst()\n            if dtdst is None:\n                raise ValueError(\"fromutc(): dt.dst gave inconsistent \"\n                                 \"results; cannot convert\")","sourceCodeStart":1319,"sourceCodeEnd":1355,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pydatetime.py#L1319-L1355","documentation":"Raised by tzinfo.fromutc() when the passed datetime's tzinfo attribute is not the same object as the tzinfo instance on which fromutc was called. The default fromutc algorithm assumes dt is annotated with self so that dt.utcoffset() and dt.dst() resolve through this timezone; a datetime carrying a different (or no) tzinfo makes the conversion meaningless.","triggerScenarios":"tz_a.fromutc(dt) where dt.tzinfo is tz_b or None; reusing a datetime created with timezone.utc and calling my_custom_tz.fromutc(dt) without re-attaching my_custom_tz; astimezone paths on subclasses that call self.fromutc on datetimes they did not construct.","commonSituations":"Manual UTC-to-local conversion where the developer forgets dt.replace(tzinfo=tz); mixing pytz-localized datetimes with stdlib fromutc; generic converters that receive datetimes from many sources.","solutions":["Attach the tzinfo before converting: dt = dt.replace(tzinfo=tz); local = tz.fromutc(dt)","Prefer dt.astimezone(tz) which handles attachment and fold correctly for stdlib timezones","Use datetime.fromtimestamp(dt.replace(tzinfo=None).timestamp(), tz) as an alternative conversion route"],"exampleFix":"// before\nlocal = eastern.fromutc(datetime(2024,6,1,12))  # tzinfo is None -> ValueError\n\n// after\nlocal = eastern.fromutc(datetime(2024,6,1,12).replace(tzinfo=eastern))","handlingStrategy":"validation","validationCode":"if dt.tzinfo is not tz:\n    dt = dt.replace(tzinfo=tz)\nlocal = tz.fromutc(dt)","typeGuard":"def dt_attached_to(dt, tz) -> bool:\n    return dt.tzinfo is tz","tryCatchPattern":"try:\n    local = tz.fromutc(dt)\nexcept ValueError as e:\n    if 'tzinfo is not self' in str(e):\n        local = tz.fromutc(dt.replace(tzinfo=tz))\n    else:\n        raise","preventionTips":["Use dt.astimezone(tz) instead of calling fromutc manually","Attach the target tzinfo with replace() before any manual fromutc call","Keep one conversion helper so attachment rules are applied uniformly"],"tags":["datetime","timezone","valueerror","fromutc"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}