pandas-dev/pandas · error · TypeError

Already tz-aware, use tz_convert to convert.

Error message

Already tz-aware, use tz_convert to convert.

What it means

Raised by tz_localize when the data already has a tz (self.tz is not None) and a new tz is requested. tz_localize attaches a tz; once data is aware you must use tz_convert to change it, so pandas refuses to silently overwrite. TypeError.

Source

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

        0   2015-03-29 03:30:00+02:00
        1   2015-03-29 03:30:00+02:00
        dtype: datetime64[ns, Europe/Warsaw]
        """  # noqa: E501
        nonexistent_options = ("raise", "NaT", "shift_forward", "shift_backward")
        if nonexistent not in nonexistent_options and not isinstance(
            nonexistent, timedelta
        ):
            raise ValueError(
                "The nonexistent argument must be one of 'raise', "
                "'NaT', 'shift_forward', 'shift_backward' or "
                "a timedelta object"
            )

        if self.tz is not None:
            if tz is None:
                new_dates = tz_convert_from_utc(self.asi8, self.tz, reso=self._creso)
            else:
                raise TypeError("Already tz-aware, use tz_convert to convert.")
        else:
            tz = timezones.maybe_get_tz(tz)
            # Convert to UTC

            new_dates = tzconversion.tz_localize_to_utc(
                self.asi8,
                tz,
                ambiguous=ambiguous,
                nonexistent=nonexistent,
                creso=self._creso,
            )
        new_dates_dt64 = new_dates.view(f"M8[{self.unit}]")
        dtype = tz_to_dtype(tz, unit=self.unit)

        return self._simple_new(new_dates_dt64, dtype=dtype)

    # ----------------------------------------------------------------
    # Conversion Methods - Vectorized analogues of Timestamp methods

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Switch to tz_convert: idx.tz_convert(target_tz).
  2. Branch on idx.tz: tz_localize when None, tz_convert otherwise.
  3. Drop the tz first with tz_localize(None) only if you genuinely want to re-localize wall time.

Example fix

# before
idx.tz_localize('US/Eastern')  # idx already aware
# after
idx.tz_convert('US/Eastern')
Defensive patterns

Strategy: validation

Validate before calling

def set_tz(idx, target):
    if idx.tz is not None:
        return idx.tz_convert(target)
    return idx.tz_localize(target)

Type guard

def is_aware(idx) -> bool:
    return getattr(idx, 'tz', None) is not None

Try / catch

try:
    out = idx.tz_localize('UTC')
except TypeError as e:
    if 'Already tz-aware' in str(e):
        out = idx.tz_convert('UTC')
    else:
        raise

Prevention

When it happens

Trigger: Calling .tz_localize('UTC') on data that was parsed with utc=True or already localized; re-localizing in a loop; applying tz_localize to a column produced by another tz_localize.

Common situations: Generic pipeline that always calls tz_localize regardless of current state; data already in UTC from the DB but code treats it as naive.

Related errors


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