pola-rs/polars · error · TypeError

datetime time zone does not match Series timezone

Error message

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

What it means

When comparing a Series of dtype Datetime against a Python datetime.datetime, _comp checks that the datetime's tzinfo matches the Series' time_zone. If they differ (or one is tz-aware and the other naive), Polars raises TypeError rather than silently applying a possibly-wrong timezone conversion. Timezone-aware values must agree exactly with the Series' declared timezone.

Solutions

  1. Convert the Python datetime to the Series' timezone before comparing: dt.replace(tzinfo=ZoneInfo('UTC')) or dt.astimezone(ZoneInfo(series_tz)).
  2. Use Polars' own literals so the timezone is handled for you: s == pl.lit(dt) or filter with pl.col('ts') == pl.lit(dt, time_zone='UTC').
  3. For string timestamps, parse with the right tz: pl.lit('2024-01-01T00:00:00').str.to_datetime(time_zone='UTC').
  4. Normalize both sides to the same timezone first, e.g. s.dt.convert_time_zone('UTC') and an aware datetime via astimezone(ZoneInfo('UTC')).

Example fix

// before
from datetime import datetime
import polars as pl
s = pl.Series('ts', [datetime(2024, 1, 1)]).dt.replace_time_zone('UTC')
s > datetime(2024, 6, 1)  # TypeError: datetime time zone 'NoneType' does not match Series timezone 'UTC'

// after
from zoneinfo import ZoneInfo
s > datetime(2024, 6, 1, tzinfo=ZoneInfo('UTC'))
Defensive patterns

Strategy: validation

Validate before calling

from datetime import datetime
from zoneinfo import ZoneInfo
if dt.tzinfo is None:
    raise ValueError('datetime must be timezone-aware to compare with tz-aware Series')
if str(dt.tzinfo) != str(series_tz):
    dt = dt.astimezone(ZoneInfo(series_tz))

Type guard

def tz_matches(dt: datetime, series_tz: str | None) -> bool:
    return str(dt.tzinfo) == str(series_tz)

Try / catch

try:
    result = s > dt
except TypeError as e:
    if 'does not match Series timezone' in str(e):
        tz = s.dtype.time_zone
        result = s > dt.astimezone(ZoneInfo(tz))
    else:
        raise

Prevention

When it happens

Trigger: Comparing a tz-aware Datetime Series with a datetime built with a different tzinfo (e.g. Series tz 'UTC' vs datetime with zoneinfo.ZoneInfo('America/New_York')), or comparing a tz-aware Series against a naive datetime.datetime (tzinfo None), via any of ==, !=, <, >, <=, >=.

Common situations: Constructing datetimes with datetime.now() (naive) and comparing against a UTC-series parsed from timestamps; mixing ZoneInfo regions with a Series stored in UTC after read_parquet/scan_csv; hard-coded datetimes from configs that omit tzinfo.

Related errors


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

Appendix: source

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

        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 fe841f959e)