{"record":{"id":"57fcd648505c516c","repo":"pola-rs/polars","slug":"datetime-time-zone-other-tzinfo-r-does-not-match","errorCode":null,"errorMessage":"datetime time zone {other.tzinfo!r} does not match Series timezone {time_zone!r}","messagePattern":"datetime time zone (.+?) does not match Series timezone (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/series/series.py","lineNumber":874,"sourceCode":"\n        elif isinstance(other, float) and self.dtype.is_integer():\n            # require upcast when comparing int series to float value\n            self = self.cast(Float64)\n            f = get_ffi_func(op + \"_<>\", Float64, self._s)\n            assert f is not None\n            return self._from_pyseries(f(other))\n\n        elif isinstance(other, datetime):\n            if self.dtype == Date:\n                # require upcast when comparing date series to datetime\n                self = self.cast(Datetime(\"us\"))\n                time_unit = \"us\"\n            elif self.dtype == Datetime:\n                # Use local time zone info\n                time_zone = self.dtype.time_zone  # type: ignore[attr-defined]\n                if str(other.tzinfo) != str(time_zone):\n                    msg = f\"datetime time zone {other.tzinfo!r} does not match Series timezone {time_zone!r}\"\n                    raise TypeError(msg)\n                time_unit = self.dtype.time_unit  # type: ignore[attr-defined]\n            else:\n                msg = f\"cannot compare datetime.datetime to Series of type {self.dtype}\"\n                raise ValueError(msg)\n            ts = datetime_to_int(other, time_unit)  # type: ignore[arg-type]\n            f = get_ffi_func(op + \"_<>\", Int64, self._s)\n            assert f is not None\n            return self._from_pyseries(f(ts))\n\n        elif isinstance(other, time) and self.dtype == Time:\n            d = time_to_int(other)\n            f = get_ffi_func(op + \"_<>\", Int64, self._s)\n            assert f is not None\n            return self._from_pyseries(f(d))\n\n        elif isinstance(other, timedelta) and self.dtype == Duration:\n            time_unit = self.dtype.time_unit  # type: ignore[attr-defined]\n            td = timedelta_to_int(other, time_unit)","sourceCodeStart":856,"sourceCodeEnd":892,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/series/series.py#L856-L892","documentation":"Raised in Series._comp (py-polars/src/polars/series/series.py:874) when comparing a Datetime Series against a datetime.datetime whose tzinfo does not match the Series' dtype time zone. Polars compares datetimes as integers with a fixed zone, so the scalar's zone (including naive=None) must string-match the Series' zone exactly; otherwise it raises TypeError showing both zones.","triggerScenarios":"s = pl.Series([datetime(2024,1,1)]).dt.replace_time_zone(\"UTC\"); s > datetime(2024,6,1) — naive datetime (tzinfo None) vs 'UTC'. Also UTC series compared to datetime(..., tzinfo=ZoneInfo(\"Europe/Amsterdam\")), or a naive Series compared to an aware datetime.","commonSituations":"Mixing tz-aware and naive datetimes in filters/thresholds; data stored per-region while the threshold is constructed in local time; DST-sensitive pipelines. The check is a string comparison of zones, so equivalent zones with different names will also fail.","solutions":["Attach the SAME zone to the scalar: from zoneinfo import ZoneInfo; s > datetime(2024, 6, 1, tzinfo=ZoneInfo(\"UTC\"))","Or align the Series: s.dt.convert_time_zone(\"Europe/Amsterdam\") > local_dt (convert_time_zone keeps the instant)","For naive Series, compare with a naive datetime (no tzinfo) instead of an aware one","Centralize threshold construction so the zone always comes from the Series dtype: datetime(..., tzinfo=ZoneInfo(s.dtype.time_zone))"],"exampleFix":"# before\ns = pl.Series([datetime(2024,1,1)]).dt.replace_time_zone(\"UTC\")\ns > datetime(2024, 6, 1)  # naive scalar vs 'UTC' Series\n\n# after\nfrom zoneinfo import ZoneInfo\ns > datetime(2024, 6, 1, tzinfo=ZoneInfo(\"UTC\"))\n# or convert the Series side:\ns.dt.convert_time_zone(\"UTC\")","handlingStrategy":"validation","validationCode":"from zoneinfo import ZoneInfo\n\ndef tz_matches(s: pl.Series, dt) -> bool:\n    tz = s.dtype.time_zone if isinstance(s.dtype, pl.Datetime) else None\n    return str(dt.tzinfo) == str(tz)\n\nassert tz_matches(s, threshold), f\"zone mismatch: {threshold.tzinfo!r} vs {s.dtype.time_zone!r}\"","typeGuard":"def tz_of(s: pl.Series) -> str | None:\n    return s.dtype.time_zone if isinstance(s.dtype, pl.Datetime) else None","tryCatchPattern":"try:\n    mask = s > threshold\nexcept TypeError as e:\n    if \"does not match Series timezone\" in str(e):\n        from zoneinfo import ZoneInfo\n        threshold = threshold.replace(tzinfo=ZoneInfo(s.dtype.time_zone))\n        mask = s > threshold\n    else:\n        raise","preventionTips":["Build threshold datetimes with tzinfo derived from the Series dtype: ZoneInfo(s.dtype.time_zone)","Standardize pipelines on one canonical zone (e.g. UTC) and convert at the edges","Remember naive (tzinfo=None) never matches an aware Series and vice versa"],"tags":["polars","series","datetime","timezone","typeerror","comparison"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}