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
- To attach a tz to naive stamps: idx.tz_localize('UTC').
- To change tz of aware stamps: idx.tz_convert('US/Eastern').
- 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
- Never assign .tz; always call tz_localize/tz_convert.
- Lint for attribute assignments to .tz / .tzinfo.
- Wrap tz changes in a helper that picks localize vs convert by current tz.
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
- Cannot create a from a MultiIndex.
- Inferred frequency from passed values does not conform to…
- left and right must have the same time zone, got
- Passed data is timezone-aware, incompatible with 'tz=None'…
- Already tz-aware, use tz_convert to convert.
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)