pola-rs/polars · error

datetime time zone {other.tzinfo!r} does not match Series ti

Error message

datetime time zone {other.tzinfo!r} does not match Series timezone {time_zone!r}

What it means

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.

Source

Thrown at py-polars/src/polars/series/series.py:874

        elif isinstance(other, float) and self.dtype.is_integer():
            # require upcast when comparing int series to float value
            self = self.cast(Float64)
            f = get_ffi_func(op + "_<>", Float64, self._s)
            assert f is not None
            return self._from_pyseries(f(other))

        elif isinstance(other, datetime):
            if self.dtype == Date:
                # require upcast when comparing date series to datetime
                self = self.cast(Datetime("us"))
                time_unit = "us"
            elif self.dtype == Datetime:
                # Use local time zone info
                time_zone = self.dtype.time_zone  # type: ignore[attr-defined]
                if str(other.tzinfo) != str(time_zone):
                    msg = f"datetime time zone {other.tzinfo!r} does not match Series timezone {time_zone!r}"
                    raise TypeError(msg)
                time_unit = self.dtype.time_unit  # type: ignore[attr-defined]
            else:
                msg = f"cannot compare datetime.datetime to Series of type {self.dtype}"
                raise ValueError(msg)
            ts = datetime_to_int(other, time_unit)  # type: ignore[arg-type]
            f = get_ffi_func(op + "_<>", Int64, self._s)
            assert f is not None
            return self._from_pyseries(f(ts))

        elif isinstance(other, time) and self.dtype == Time:
            d = time_to_int(other)
            f = get_ffi_func(op + "_<>", Int64, self._s)
            assert f is not None
            return self._from_pyseries(f(d))

        elif isinstance(other, timedelta) and self.dtype == Duration:
            time_unit = self.dtype.time_unit  # type: ignore[attr-defined]
            td = timedelta_to_int(other, time_unit)

View on GitHub (pinned to df599052da)

Solutions

  1. Attach the SAME zone to the scalar: from zoneinfo import ZoneInfo; s > datetime(2024, 6, 1, tzinfo=ZoneInfo("UTC"))
  2. Or align the Series: s.dt.convert_time_zone("Europe/Amsterdam") > local_dt (convert_time_zone keeps the instant)
  3. For naive Series, compare with a naive datetime (no tzinfo) instead of an aware one
  4. Centralize threshold construction so the zone always comes from the Series dtype: datetime(..., tzinfo=ZoneInfo(s.dtype.time_zone))

Example fix

# before
s = pl.Series([datetime(2024,1,1)]).dt.replace_time_zone("UTC")
s > datetime(2024, 6, 1)  # naive scalar vs 'UTC' Series

# after
from zoneinfo import ZoneInfo
s > datetime(2024, 6, 1, tzinfo=ZoneInfo("UTC"))
# or convert the Series side:
s.dt.convert_time_zone("UTC")
Defensive patterns

Strategy: validation

Validate before calling

from zoneinfo import ZoneInfo

def tz_matches(s: pl.Series, dt) -> bool:
    tz = s.dtype.time_zone if isinstance(s.dtype, pl.Datetime) else None
    return str(dt.tzinfo) == str(tz)

assert tz_matches(s, threshold), f"zone mismatch: {threshold.tzinfo!r} vs {s.dtype.time_zone!r}"

Type guard

def tz_of(s: pl.Series) -> str | None:
    return s.dtype.time_zone if isinstance(s.dtype, pl.Datetime) else None

Try / catch

try:
    mask = s > threshold
except TypeError as e:
    if "does not match Series timezone" in str(e):
        from zoneinfo import ZoneInfo
        threshold = threshold.replace(tzinfo=ZoneInfo(s.dtype.time_zone))
        mask = s > threshold
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16). Data as JSON: /api/errors/57fcd648505c516c. Report an issue: GitHub.