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 inside DatetimeArray._assert_tzawareness_compat when comparing a tz-naive DatetimeIndex/array (self.tz is None) against an operand that carries tzinfo (a tz-aware Timestamp, Series, or DatetimeIndex). Pandas refuses the operation because wall-time vs absolute-time comparison is ambiguous; the comparison would silently produce wrong results, so it errors hard. It is a TypeError.

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 None

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Align both sides to the same awareness: localize the naive side with tz_localize('UTC') before comparing.
  2. If one side is truly UTC underneath, tz_localize then tz_convert it to the other side's tz.
  3. If you want a wall-time comparison, strip the aware side via tz_localize(None) (only if you accept losing absolute-time meaning).
  4. Audit df columns with df[col].dt.tz before any merge/join/comparison.

Example fix

# before
naive < pd.Timestamp('2020-01-01', tz='UTC')
# after
naive.tz_localize('UTC') < pd.Timestamp('2020-01-01', tz='UTC')
Defensive patterns

Strategy: validation

Validate before calling

def assert_same_awareness(left, right):
    l_tz = getattr(getattr(left, 'dtype', None), 'tz', None) or getattr(left, 'tzinfo', None)
    r_tz = getattr(getattr(right, 'dtype', None), 'tz', None) or getattr(right, 'tzinfo', None)
    if (l_tz is None) != (r_tz is None):
        raise TypeError(f'awareness mismatch: left tz={l_tz}, right tz={r_tz}')

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:
    result = left < right
except TypeError as e:
    if 'tz-naive and tz-aware' in str(e):
        # localize the naive side to the aware side's tz, then retry
        ...
    raise

Prevention

When it happens

Trigger: Comparisons (==, <, >, isin, merge, between) where the left side is tz-naive and the right is tz-aware, e.g. naive_dti < pd.Timestamp('2020-01-01', tz='UTC'), or a DataFrame with a naive datetime column merged against an aware one. Also reached through Series.dt operations that delegate to the array's compare path.

Common situations: Loading data from CSV/SQL yields tz-naive timestamps while a second source (API, database with tz) is aware; user converts one column with tz_localize but forgets the other; mixing pd.Timestamp('now') (aware) with parsed strings (naive).

Related errors


AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07). Data as JSON: /api/errors/4990abcb4084b27f. Report an issue: GitHub.