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
- 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))
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
- 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
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
- cannot compare datetime.datetime to Series of type {self.dty
- cannot treat Series of type {s.dtype} as indices
- {how!r} strategy is not supported for {qualified_type_name(e
- time zone of dtype ({dtype_tz!r}) differs from time zone of
- "{operator!r}" comparison not supported for LazyFrame object
AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16).
Data as JSON: /api/errors/57fcd648505c516c.
Report an issue: GitHub.