{"record":{"id":"3c9d53fbfcbb44d5","repo":"python/cpython","slug":"cannot-compare-naive-and-aware-datetimes","errorCode":null,"errorMessage":"cannot compare naive and aware datetimes","messagePattern":"cannot compare naive and aware datetimes","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"Lib/_pydatetime.py","lineNumber":2323,"sourceCode":"            if allow_mixed:\n                if myoff != self.replace(fold=not self.fold).utcoffset():\n                    return 2\n                if otoff != other.replace(fold=not other.fold).utcoffset():\n                    return 2\n            base_compare = myoff == otoff\n\n        if base_compare:\n            return _cmp((self._year, self._month, self._day,\n                         self._hour, self._minute, self._second,\n                         self._microsecond),\n                        (other._year, other._month, other._day,\n                         other._hour, other._minute, other._second,\n                         other._microsecond))\n        if myoff is None or otoff is None:\n            if allow_mixed:\n                return 2 # arbitrary non-zero value\n            else:\n                raise TypeError(\"cannot compare naive and aware datetimes\")\n        # XXX What follows could be done more efficiently...\n        diff = self - other     # this will take offsets into account\n        if diff.days < 0:\n            return -1\n        return diff and 1 or 0\n\n    def __add__(self, other):\n        \"Add a datetime and a timedelta.\"\n        if not isinstance(other, timedelta):\n            return NotImplemented\n        delta = timedelta(self.toordinal(),\n                          hours=self._hour,\n                          minutes=self._minute,\n                          seconds=self._second,\n                          microseconds=self._microsecond)\n        delta += other\n        hour, rem = divmod(delta.seconds, 3600)\n        minute, second = divmod(rem, 60)","sourceCodeStart":2305,"sourceCodeEnd":2341,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pydatetime.py#L2305-L2341","documentation":"Ordering comparisons (<, >, <=, >=) between a naive datetime (no tzinfo) and an aware datetime (with tzinfo) are forbidden because there is no defined answer without knowing the naive side's zone. Equality (==) is allowed and returns False, but ordering raises TypeError.","triggerScenarios":"Comparing datetime.now() (naive) with datetime.now(timezone.utc) (aware); sorting a list mixing DB naive timestamps with API aware timestamps; min()/max() over mixed collections.","commonSituations":"Legacy databases storing naive UTC datetimes while modern APIs emit ISO strings with offsets; mixing Django USE_TZ=True objects with manual naive ones; tests asserting now() < fetched_ts across systems.","solutions":["Attach a timezone to the naive side: naive.replace(tzinfo=timezone.utc) (only if it truly is UTC)","Make both naive consistently: dt_aware.replace(tzinfo=None) or dt_aware.astimezone().replace(tzinfo=None) for local","Standardize the codebase on aware datetimes (UTC) end-to-end","If mixing is intentional, compare timestamps as .timestamp() floats"],"exampleFix":"// before\nif datetime.now() < row['created_at_aware']: ...\n\n# after\nif datetime.now(timezone.utc) < row['created_at_aware']: ...","handlingStrategy":"validation","validationCode":"from datetime import datetime, timezone\n\ndef ensure_aware(dt: datetime, *, assume_utc=True) -> datetime:\n    if dt.tzinfo is None:\n        return dt.replace(tzinfo=timezone.utc if assume_utc else None)\n    return dt\n\nif ensure_aware(a) < ensure_aware(b): ...","typeGuard":"from datetime import datetime\n\ndef is_aware(dt: datetime) -> bool:\n    return dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) is not None","tryCatchPattern":"try:\n    _ = a < b\nexcept TypeError:\n    a, b = ensure_aware(a), ensure_aware(b)\n    _ = a < b","preventionTips":["Adopt aware-UTC everywhere (e.g. USE_TZ=True)","Assert awareness in test fixtures","Check dt.tzinfo is not None before comparisons at boundaries"],"tags":["python","datetime","timezone","type-error","comparison"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}