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
Raised by DatetimeArray._assert_tzawareness_compat when comparing (or otherwise combining) a tz-naive self with a tz-aware other that has a non-None tzinfo. Pandas forbids silent comparison across tz-awareness because the result semantics would be undefined (naive has no UTC anchor). The same string with a trailing period appears at [303]; this variant (self naive, other aware) is the more common direction.
Solutions
- Decide a single tz convention: localize the naive side with `obj.dt.tz_localize(tz)` or drop the aware side with `obj.dt.tz_localize(None)` before comparing.
- If both should be the same tz, `aware_series.dt.tz_convert(tz)` to align and then compare.
- When comparing to a reference Timestamp, build it with matching awareness: `pd.Timestamp.now(tz=col.dt.tz)` for aware cols, or `pd.Timestamp.now().tz_localize(None)` for naive cols.
- Normalize at ingest: pick UTC throughout the pipeline and convert only at presentation.
Example fix
// before
mask = df['naive_ts'] < pd.Timestamp.now(tz='UTC')
// after
mask = df['naive_ts'].dt.tz_localize('UTC') < pd.Timestamp.now(tz='UTC') Defensive patterns
Strategy: validation
Validate before calling
import pandas as pd
def align_tz_for_compare(left, right):
ltz = getattr(left.dtype, 'tz', None) or getattr(left, 'tzinfo', None)
rtz = getattr(right.dtype, 'tz', None) or getattr(right, 'tzinfo', None)
if ltz is None and rtz is not None:
left = left.dt.tz_localize(rtz) if hasattr(left, 'dt') else left.tz_localize(rtz)
return left, right Type guard
def both_same_awareness(a, b) -> bool:
az = getattr(getattr(a, 'dtype', None), 'tz', getattr(a, 'tzinfo', None))
bz = getattr(getattr(b, 'dtype', None), 'tz', getattr(b, 'tzinfo', None))
return (az is None) == (bz is None) Try / catch
try:
result = a < b
except TypeError as e:
if 'tz-naive and tz-aware' in str(e):
b = b.dt.tz_localize(a.dt.tz) if hasattr(b, 'dt') else b
result = a < b
else:
raise Prevention
- Pick one tz policy for the whole pipeline (e.g. everything tz-aware UTC).
- Always build reference Timestamps with explicit tz matching the data.
- Validate tz awareness right after read_csv/read_sql.
When it happens
Trigger: Comparison operators (`==`, `<`, `>`, etc.) between a tz-naive DatetimeIndex/Series and a tz-aware Timestamp, datetime, DatetimeIndex, or Series. Also triggered by set operations (union, intersection) and alignment that ultimately compare elements.
Common situations: Joining/merging two datetime columns where one was tz_localized and the other was not. Comparing a column to `pd.Timestamp.now()` (which is tz-aware) when the column is tz-naive. Mixing data from sources where some attach UTC offsets and some do not.
Related errors
- Cannot compare tz-naive and tz-aware datetime-like objects
- Already tz-aware, use tz_convert to convert.
- Cannot convert tz-naive timestamps, use tz_localize to…
- Cannot pass both a timezone-aware dtype and tz=None
- cannot supply both a tz and a dtype with a tz
AI-assisted analysis of pandas-dev/pandas@3b7651241d (2026-08-11).
Data as JSON: /api/errors/4990abcb4084b27f.
Report an issue: GitHub.
Appendix: source
Thrown at pandas/core/arrays/datetimes.py:782
)
# -----------------------------------------------------------------
# Comparison Methods
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 NoneView on GitHub (pinned to 3b7651241d)