pandas-dev/pandas · error · AttributeError
Cannot directly set timezone. Use tz_localize() or tz_conver
Error message
Cannot directly set timezone. Use tz_localize() or tz_convert() as appropriate
What it means
Raised by the tz property setter of DatetimeArray/DatetimeIndex. The tz attribute is read-only because timezone changes are not in-place conversions — localizing (adding a tz) and converting (shifting across zones) are different operations with different semantics (and DST ambiguity handling), so they each get their own method.
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 71959b8cb9)
Solutions
- To attach a tz to naive data: idx = idx.tz_localize('UTC').
- To switch zones on tz-aware data: idx = idx.tz_convert('US/Eastern').
- To strip a tz: idx = idx.tz_localize(None).
Example fix
# before
idx.tz = 'UTC'
# after (naive -> aware)
idx = idx.tz_localize('UTC')
# or aware -> different zone
idx = idx.tz_convert('UTC') Defensive patterns
Strategy: validation
Validate before calling
# do not assign; route to the correct API if want_attach_tz: idx = idx.tz_localize(tz) elif want_change_tz: idx = idx.tz_convert(tz) elif want_drop_tz: idx = idx.tz_localize(None)
Try / catch
try:
idx.tz = tz
except AttributeError as e:
if 'Cannot directly set timezone' in str(e):
idx = idx.tz_localize(tz) if idx.tz is None else idx.tz_convert(tz)
else: raise Prevention
- Treat .tz as read-only.
- Lint for 'index.tz =' / '.tz =' assignments.
When it happens
Trigger: idx.tz = 'UTC' on a DatetimeIndex or DatetimeArray-backed Series.dt; series.index.tz = pytz.UTC; df.index.tz = None.
Common situations: Users from other libraries (or older pandas idioms) assuming index.tz is assignable. Quick interactive attempts to 'just add a zone'. Tutorial code copied from a non-pandas source.
Related errors
- Passed data is timezone-aware, incompatible with 'tz=None'.
- Cannot compare tz-naive and tz-aware datetime-like objects.
- Cannot compare tz-naive and tz-aware datetime-like objects
- Cannot convert tz-naive timestamps, use tz_localize to local
- The nonexistent argument must be one of 'raise', 'NaT', 'shi
AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07).
Data as JSON: /api/errors/27254cec19d7443f.
Report an issue: GitHub.