{"record":{"id":"4990abcb4084b27f","repo":"pandas-dev/pandas","slug":"cannot-compare-tz-naive-and-tz-aware-datetime-like","errorCode":null,"errorMessage":"Cannot compare tz-naive and tz-aware datetime-like objects.","messagePattern":"Cannot compare tz-naive and tz-aware datetime-like objects\\.","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"pandas/core/arrays/datetimes.py","lineNumber":782,"sourceCode":"        )\n\n    # -----------------------------------------------------------------\n    # Comparison Methods\n\n    def _assert_tzawareness_compat(self, other) -> None:\n        # adapted from _Timestamp._assert_tzawareness_compat\n        other_tz = getattr(other, \"tzinfo\", None)\n        other_dtype = getattr(other, \"dtype\", None)\n\n        if isinstance(other_dtype, DatetimeTZDtype):\n            # Get tzinfo from Series dtype\n            other_tz = other.dtype.tz\n        if other is NaT:\n            # pd.NaT quacks both aware and naive\n            pass\n        elif self.tz is None:\n            if other_tz is not None:\n                raise TypeError(\n                    \"Cannot compare tz-naive and tz-aware datetime-like objects.\"\n                )\n        elif other_tz is None:\n            raise TypeError(\n                \"Cannot compare tz-naive and tz-aware datetime-like objects\"\n            )\n\n    # -----------------------------------------------------------------\n    # Arithmetic Methods\n\n    def _add_offset(self, offset: BaseOffset) -> Self:\n        assert not isinstance(offset, Tick)\n\n        # For pure-timedelta DateOffset with tz-aware data, add to UTC values\n        # directly to avoid nonexistent/ambiguous time errors from\n        # re-localizing wall-time results near DST (GH#28610).\n        if (\n            self.tz is not None","sourceCodeStart":764,"sourceCodeEnd":800,"githubUrl":"https://github.com/pandas-dev/pandas/blob/3b7651241d4da534b3559b60ef128e1c34f54116/pandas/core/arrays/datetimes.py#L764-L800","documentation":"Raised by DatetimeArray._assert_tzawareness_compat when comparing (or otherwise combining) a tz-naive self with a tz-aware other that has a non-None tzinfo. Pandas forbids silent comparison across tz-awareness because the result semantics would be undefined (naive has no UTC anchor). The same string with a trailing period appears at [303]; this variant (self naive, other aware) is the more common direction.","triggerScenarios":"Comparison operators (`==`, `<`, `>`, etc.) between a tz-naive DatetimeIndex/Series and a tz-aware Timestamp, datetime, DatetimeIndex, or Series. Also triggered by set operations (union, intersection) and alignment that ultimately compare elements.","commonSituations":"Joining/merging two datetime columns where one was tz_localized and the other was not. Comparing a column to `pd.Timestamp.now()` (which is tz-aware) when the column is tz-naive. Mixing data from sources where some attach UTC offsets and some do not.","solutions":["Decide a single tz convention: localize the naive side with `obj.dt.tz_localize(tz)` or drop the aware side with `obj.dt.tz_localize(None)` before comparing.","If both should be the same tz, `aware_series.dt.tz_convert(tz)` to align and then compare.","When comparing to a reference Timestamp, build it with matching awareness: `pd.Timestamp.now(tz=col.dt.tz)` for aware cols, or `pd.Timestamp.now().tz_localize(None)` for naive cols.","Normalize at ingest: pick UTC throughout the pipeline and convert only at presentation."],"exampleFix":"// before\nmask = df['naive_ts'] < pd.Timestamp.now(tz='UTC')\n\n// after\nmask = df['naive_ts'].dt.tz_localize('UTC') < pd.Timestamp.now(tz='UTC')","handlingStrategy":"validation","validationCode":"import pandas as pd\n\ndef align_tz_for_compare(left, right):\n    ltz = getattr(left.dtype, 'tz', None) or getattr(left, 'tzinfo', None)\n    rtz = getattr(right.dtype, 'tz', None) or getattr(right, 'tzinfo', None)\n    if ltz is None and rtz is not None:\n        left = left.dt.tz_localize(rtz) if hasattr(left, 'dt') else left.tz_localize(rtz)\n    return left, right","typeGuard":"def both_same_awareness(a, b) -> bool:\n    az = getattr(getattr(a, 'dtype', None), 'tz', getattr(a, 'tzinfo', None))\n    bz = getattr(getattr(b, 'dtype', None), 'tz', getattr(b, 'tzinfo', None))\n    return (az is None) == (bz is None)","tryCatchPattern":"try:\n    result = a < b\nexcept TypeError as e:\n    if 'tz-naive and tz-aware' in str(e):\n        b = b.dt.tz_localize(a.dt.tz) if hasattr(b, 'dt') else b\n        result = a < b\n    else:\n        raise","preventionTips":["Pick one tz policy for the whole pipeline (e.g. everything tz-aware UTC).","Always build reference Timestamps with explicit tz matching the data.","Validate tz awareness right after read_csv/read_sql."],"tags":["datetime","timezone","comparison","tz-naive"],"backgroundTag":null,"analyzedSha":"3b7651241d4da534b3559b60ef128e1c34f54116","analyzedAt":"2026-08-11T22:10:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}