pandas-dev/pandas · error · AttributeError

Cannot directly set timezone. Use tz_localize() or…

Error message

Cannot directly set timezone. Use tz_localize() or tz_convert() as appropriate

What it means

Raised as AttributeError by the DatetimeArray.tz property setter (GH 3746). Assigning idx.tz = 'UTC' would be ambiguous between localize (attach tz to naive stamps) and convert (re-express aware stamps in another tz), each of which has DST/ambiguous-time options, so pandas forbids direct assignment and routes users to the explicit tz_localize/tz_convert methods.

Solutions

  1. To attach a tz to naive stamps: idx.tz_localize('UTC').
  2. To change tz of aware stamps: idx.tz_convert('US/Eastern').
  3. To drop tz: idx.tz_localize(None).

Example fix

// before
idx = pd.date_range('2020-01-01', periods=3)
idx.tz = 'UTC'  # AttributeError: Cannot directly set timezone

// after
idx = idx.tz_localize('UTC')
Defensive patterns

Strategy: type-guard

Validate before calling

def set_tz(idx, tz):
    # tz is None -> drop; tz is str -> attach
    if idx.tz is None:
        return idx.tz_localize(tz) if tz is not None else idx
    return idx.tz_convert(tz) if tz is not None else idx.tz_localize(None)

Type guard

def is_tz_assignable_property(name) -> bool:
    return name != "tz"  # block direct .tz assignment

Try / catch

try:
    idx.tz = "UTC"
except AttributeError as e:
    if "Cannot directly set timezone" in str(e):
        idx = idx.tz_localize("UTC")
    else:
        raise

Prevention

When it happens

Trigger: Attempting idx.tz = 'UTC', idx.tz = None, or any assignment to the .tz property of a DatetimeIndex/DatetimeArray (or Series.dt.tz assignment where backed by this array).

Common situations: Migrating from older pandas or other libraries where tz was settable; typos that confuse .tz with .tz_localize(); code generators that synthesize property setters.

Related errors


AI-assisted analysis of pandas-dev/pandas@3b7651241d (2026-08-11). Data as JSON: /api/errors/27254cec19d7443f. Report an issue: GitHub.

Appendix: source

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

        dtype: datetime64[us, UTC]
        >>> s.dt.tz
        datetime.timezone.utc

        For DatetimeIndex:

        >>> idx = pd.DatetimeIndex(
        ...     ["1/1/2020 10:00:00+00:00", "2/1/2020 11:00:00+00:00"]
        ... )
        >>> idx.tz
        datetime.timezone.utc
        """  # noqa: E501
        # GH 18595
        return getattr(self.dtype, "tz", None)

    @tz.setter
    def tz(self, value):
        # GH 3746: Prevent localizing or converting the index by setting tz
        raise AttributeError(
            "Cannot directly set timezone. Use tz_localize() "
            "or tz_convert() as appropriate"
        )

    @property
    def tzinfo(self) -> tzinfo | None:
        """
        Alias for tz attribute
        """
        return self.tz

    @property  # NB: override with cache_readonly in immutable subclasses
    def is_normalized(self) -> bool:
        """
        Returns True if all of the dates are at midnight ("no time")
        """
        return is_date_array_normalized(self.asi8, self.tz, reso=self._creso)

View on GitHub (pinned to 3b7651241d)