pandas-dev/pandas · error · TypeError

DatetimeIndex has mixed timezones

Error message

DatetimeIndex has mixed timezones

What it means

Raised when constructing a DatetimeIndex from object-dtype data whose elements resolve to datetimes but disagree on tz (some aware, some naive, or multiple tzs). pandas cannot pick one tz, so when allow_object is False (the DatetimeIndex path) it refuses. TypeError.

Source

Thrown at pandas/core/arrays/datetimes.py:2862

        yearfirst=yearfirst,
        creso=abbrev_to_npy_unit(out_unit),
    )

    if tz_parsed is not None:
        # We can take a shortcut since the datetime64 numpy array
        #  is in UTC
        return result, tz_parsed
    elif result.dtype.kind == "M":
        return result, tz_parsed
    elif result.dtype == object:
        # GH#23675 when called via `pd.to_datetime`, returning an object-dtype
        #  array is allowed.  When called via `pd.DatetimeIndex`, we can
        #  only accept datetime64 dtype, so raise TypeError if object-dtype
        #  is returned, as that indicates the values can be recognized as
        #  datetimes but they have conflicting timezones/awareness
        if allow_object:
            return result, tz_parsed
        raise TypeError("DatetimeIndex has mixed timezones")
    else:  # pragma: no cover
        # GH#23675 this TypeError should never be hit, whereas the TypeError
        #  in the object-dtype branch above is reachable.
        raise TypeError(result)


def maybe_convert_dtype(data, copy: bool, tz: tzinfo | None = None):
    """
    Convert data based on dtype conventions, issuing
    errors where appropriate.

    Parameters
    ----------
    data : np.ndarray or pd.Index
    copy : bool
    tz : tzinfo or None, default None

    Returns

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Normalize all elements to one tz before constructing, e.g. localize naive ones and tz_convert the rest to UTC.
  2. Split aware vs naive rows, localize/convert separately, then concatenate.
  3. At ingestion, enforce utc=True in pd.to_datetime so everything becomes UTC consistently.

Example fix

# before
pd.DatetimeIndex([pd.Timestamp('2020', tz='UTC'), pd.Timestamp('2021')])
# after
pd.DatetimeIndex([pd.Timestamp('2020', tz='UTC'), pd.Timestamp('2021').tz_localize('UTC')])
Defensive patterns

Strategy: validation

Validate before calling

def normalize_tzs(ts_list, target='UTC'):
    out = []
    for ts in ts_list:
        ts = pd.Timestamp(ts)
        if ts.tzinfo is None:
            ts = ts.tz_localize(target)
        else:
            ts = ts.tz_convert(target)
        out.append(ts)
    return out

Type guard

def all_same_awareness(ts_list) -> bool:
    tzs = {pd.Timestamp(t).tzinfo is not None for t in ts_list}
    return len(tzs) == 1

Prevention

When it happens

Trigger: pd.DatetimeIndex([...]) where the list mixes aware and naive Timestamps/datetime.datetime; pd.to_datetime on a Series of Python datetimes that include some with tzinfo and some without.

Common situations: Concatenating data from sources with inconsistent tz handling; JSON/DB rows where some rows carry tz and others don't; user input mixed with default datetime.now().

Related errors


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