{"record":{"id":"6ed54d5338807496","repo":"python/cpython","slug":"cannot-mix-naive-and-timezone-aware-time","errorCode":null,"errorMessage":"cannot mix naive and timezone-aware time","messagePattern":"cannot mix naive and timezone-aware time","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"Lib/_pydatetime.py","lineNumber":2372,"sourceCode":"            if isinstance(other, timedelta):\n                return self + -other\n            return NotImplemented\n\n        days1 = self.toordinal()\n        days2 = other.toordinal()\n        secs1 = self._second + self._minute * 60 + self._hour * 3600\n        secs2 = other._second + other._minute * 60 + other._hour * 3600\n        base = timedelta(days1 - days2,\n                         secs1 - secs2,\n                         self._microsecond - other._microsecond)\n        if self._tzinfo is other._tzinfo:\n            return base\n        myoff = self.utcoffset()\n        otoff = other.utcoffset()\n        if myoff == otoff:\n            return base\n        if myoff is None or otoff is None:\n            raise TypeError(\"cannot mix naive and timezone-aware time\")\n        return base + otoff - myoff\n\n    def __hash__(self):\n        if self._hashcode == -1:\n            if self.fold:\n                t = self.replace(fold=0)\n            else:\n                t = self\n            tzoff = t.utcoffset()\n            if tzoff is None:\n                self._hashcode = hash(t._getstate()[0])\n            else:\n                days = _ymd2ord(self.year, self.month, self.day)\n                seconds = self.hour * 3600 + self.minute * 60 + self.second\n                self._hashcode = hash(timedelta(days, seconds, self.microsecond) - tzoff)\n        return self._hashcode\n\n    # Pickle support.","sourceCodeStart":2354,"sourceCodeEnd":2390,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pydatetime.py#L2354-L2390","documentation":"datetime.__sub__ computes a base timedelta from wall-clock differences, then adjusts for utcoffset when the two tzinfos differ. If either utcoffset() returns None (a naive datetime mixed against an aware one under differing tzinfo), the subtraction is undefined and raises TypeError('cannot mix naive and timezone-aware time').","triggerScenarios":"aware_dt - naive_dt or naive_dt - aware_dt where the naive side has tzinfo None; also aware datetimes whose custom tzinfo.utcoffset() returns None (a naive-behaving tzinfo) mixed with real ones.","commonSituations":"Computing durations between DB naive timestamps and current aware time: now(timezone.utc) - row['created_at']; custom tzinfo subclasses that return None from utcoffset; mixed ORM sessions with and without timezone support.","solutions":["Make the naive side aware with its true zone: naive.replace(tzinfo=timezone.utc)","Or strip awareness after converting: aware.astimezone(timezone.utc).replace(tzinfo=None)","For durations, compare .timestamp() values instead of subtracting objects","Fix custom tzinfo subclasses so utcoffset() never returns None for aware use"],"exampleFix":"// before\nage = datetime.now(timezone.utc) - created_at_naive\n\n# after\nage = datetime.now(timezone.utc) - created_at_naive.replace(tzinfo=timezone.utc)","handlingStrategy":"validation","validationCode":"from datetime import datetime, timezone\n\ndef duration_between(a: datetime, b: datetime) -> float:\n    return a.timestamp() - b.timestamp()  # naive assumed local; aware exact","typeGuard":"from datetime import datetime\n\ndef both_same_awareness(a, b) -> bool:\n    return (a.tzinfo is None) == (b.tzinfo is None)","tryCatchPattern":"try:\n    delta = a - b\nexcept TypeError:\n    delta = a.replace(tzinfo=None) - b.replace(tzinfo=None)  # wall-clock fallback","preventionTips":["Attach the true zone to naive DB timestamps before arithmetic","Standardize storage as aware UTC","Use .timestamp() for cross-system durations"],"tags":["python","datetime","timezone","type-error","arithmetic"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}