{"record":{"id":"27254cec19d7443f","repo":"pandas-dev/pandas","slug":"cannot-directly-set-timezone-use-tz-localize-or","errorCode":null,"errorMessage":"Cannot directly set timezone. Use tz_localize() or tz_convert() as appropriate","messagePattern":"Cannot directly set timezone\\. Use tz_localize\\(\\) or tz_convert\\(\\) as appropriate","errorType":"exception","errorClass":"AttributeError","httpStatus":null,"severity":"error","filePath":"pandas/core/arrays/datetimes.py","lineNumber":651,"sourceCode":"        dtype: datetime64[us, UTC]\n        >>> s.dt.tz\n        datetime.timezone.utc\n\n        For DatetimeIndex:\n\n        >>> idx = pd.DatetimeIndex(\n        ...     [\"1/1/2020 10:00:00+00:00\", \"2/1/2020 11:00:00+00:00\"]\n        ... )\n        >>> idx.tz\n        datetime.timezone.utc\n        \"\"\"  # noqa: E501\n        # GH 18595\n        return getattr(self.dtype, \"tz\", None)\n\n    @tz.setter\n    def tz(self, value):\n        # GH 3746: Prevent localizing or converting the index by setting tz\n        raise AttributeError(\n            \"Cannot directly set timezone. Use tz_localize() \"\n            \"or tz_convert() as appropriate\"\n        )\n\n    @property\n    def tzinfo(self) -> tzinfo | None:\n        \"\"\"\n        Alias for tz attribute\n        \"\"\"\n        return self.tz\n\n    @property  # NB: override with cache_readonly in immutable subclasses\n    def is_normalized(self) -> bool:\n        \"\"\"\n        Returns True if all of the dates are at midnight (\"no time\")\n        \"\"\"\n        return is_date_array_normalized(self.asi8, self.tz, reso=self._creso)\n","sourceCodeStart":633,"sourceCodeEnd":669,"githubUrl":"https://github.com/pandas-dev/pandas/blob/3b7651241d4da534b3559b60ef128e1c34f54116/pandas/core/arrays/datetimes.py#L633-L669","documentation":"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.","triggerScenarios":"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).","commonSituations":"Migrating from older pandas or other libraries where tz was settable; typos that confuse .tz with .tz_localize(); code generators that synthesize property setters.","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)."],"exampleFix":"// before\nidx = pd.date_range('2020-01-01', periods=3)\nidx.tz = 'UTC'  # AttributeError: Cannot directly set timezone\n\n// after\nidx = idx.tz_localize('UTC')","handlingStrategy":"type-guard","validationCode":"def set_tz(idx, tz):\n    # tz is None -> drop; tz is str -> attach\n    if idx.tz is None:\n        return idx.tz_localize(tz) if tz is not None else idx\n    return idx.tz_convert(tz) if tz is not None else idx.tz_localize(None)","typeGuard":"def is_tz_assignable_property(name) -> bool:\n    return name != \"tz\"  # block direct .tz assignment","tryCatchPattern":"try:\n    idx.tz = \"UTC\"\nexcept AttributeError as e:\n    if \"Cannot directly set timezone\" in str(e):\n        idx = idx.tz_localize(\"UTC\")\n    else:\n        raise","preventionTips":["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."],"tags":["pandas","datetimeindex","timezone","attribute-error","gh3746"],"backgroundTag":null,"analyzedSha":"3b7651241d4da534b3559b60ef128e1c34f54116","analyzedAt":"2026-08-11T22:10:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}