pandas-dev/pandas · error · TypeError
Cannot compare tz-naive and tz-aware datetime-like objects
Error message
Cannot compare tz-naive and tz-aware datetime-like objects
What it means
The mirror case of error 280 from the same _assert_tzawareness_compat method: here self (the DatetimeIndex/array) is tz-aware (self.tz is not None) but the other operand is tz-naive (other_tz is None, and not NaT). Pandas raises because a tz-aware value cannot be meaningfully compared against a wall-time-only value. TypeError.
Source
Thrown at pandas/core/arrays/datetimes.py:786
def _assert_tzawareness_compat(self, other) -> None:
# adapted from _Timestamp._assert_tzawareness_compat
other_tz = getattr(other, "tzinfo", None)
other_dtype = getattr(other, "dtype", None)
if isinstance(other_dtype, DatetimeTZDtype):
# Get tzinfo from Series dtype
other_tz = other.dtype.tz
if other is NaT:
# pd.NaT quacks both aware and naive
pass
elif self.tz is None:
if other_tz is not None:
raise TypeError(
"Cannot compare tz-naive and tz-aware datetime-like objects."
)
elif other_tz is None:
raise TypeError(
"Cannot compare tz-naive and tz-aware datetime-like objects"
)
# -----------------------------------------------------------------
# Arithmetic Methods
def _add_offset(self, offset: BaseOffset) -> Self:
assert not isinstance(offset, Tick)
# For pure-timedelta DateOffset with tz-aware data, add to UTC values
# directly to avoid nonexistent/ambiguous time errors from
# re-localizing wall-time results near DST (GH#28610).
if (
self.tz is not None
and isinstance(offset, RelativeDeltaOffset)
and not offset._use_relativedelta
):
res_values = self._ndarray + offset._pd_timedeltaView on GitHub (pinned to 71959b8cb9)
Solutions
- Localize the naive operand to the aware side's tz, e.g. pd.Timestamp('2020-01-01').tz_localize(aware_dti.tz).
- Or tz_convert the aware side to the naive side's implied tz after localizing the naive side.
- If wall-time comparison is intended, tz_localize(None) on the aware side (document the trade-off).
- Normalize tz across the pipeline at ingestion time so all datetime columns share one awareness.
Example fix
# before
aware_dti > pd.Timestamp('2020-01-01')
# after
aware_dti > pd.Timestamp('2020-01-01').tz_localize(aware_dti.tz) Defensive patterns
Strategy: validation
Validate before calling
def localize_operand(op, target_tz):
op_tz = getattr(getattr(op, 'dtype', None), 'tz', None) or getattr(op, 'tzinfo', None)
if op_tz is None and target_tz is not None:
return op.tz_localize(target_tz) if hasattr(op, 'tz_localize') else pd.Timestamp(op).tz_localize(target_tz)
return op Type guard
def is_tz_aware(x) -> bool:
dt = getattr(x, 'dtype', None)
return getattr(dt, 'tz', None) is not None or getattr(x, 'tzinfo', None) is not None Try / catch
try:
out = aware_dti > scalar
except TypeError as e:
if 'tz-naive and tz-aware' in str(e):
scalar = pd.Timestamp(scalar).tz_localize(aware_dti.tz)
out = aware_dti > scalar
else:
raise Prevention
- Wrap incoming naive scalars in tz_localize(target) before any comparison.
- Keep a single canonical tz for the whole pipeline.
- Document which columns are aware in your schema.
When it happens
Trigger: A tz-aware DatetimeIndex/Series compared with a naive Timestamp, datetime.datetime, or naive index, e.g. aware_dti > pd.Timestamp('2020-01-01'), or filtering an aware Series with a naive scalar. Reached through ==, <, >, merge keys, isin, .between.
Common situations: Column localized to UTC for storage, then filtered with naive timestamps from user input; joining an aware index against a naive one produced by pd.date_range without tz; mixing datetime.datetime.now() (naive) with tz-aware data.
Related errors
- Cannot compare tz-naive and tz-aware datetime-like objects.
- Cannot convert tz-naive timestamps, use tz_localize to local
- The nonexistent argument must be one of 'raise', 'NaT', 'shi
- Already tz-aware, use tz_convert to convert.
- DatetimeIndex has mixed timezones
AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07).
Data as JSON: /api/errors/6e3805f65b7e107c.
Report an issue: GitHub.