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

  1. 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.
  2. If both should be the same tz, `aware_series.dt.tz_convert(tz)` to align and then compare.
  3. 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.
  4. 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

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


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 None

View on GitHub (pinned to 3b7651241d)