{"record":{"id":"43021e75a4f0f5b4","repo":"pandas-dev/pandas","slug":"cannot-convert-tz-naive-timestamps-use-tz-localiz","errorCode":null,"errorMessage":"Cannot convert tz-naive timestamps, use tz_localize to localize","messagePattern":"Cannot convert tz-naive timestamps, use tz_localize to localize","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"pandas/core/arrays/arrow/array.py","lineNumber":4307,"sourceCode":"            \"shift_backward\": \"earliest\",\n            \"shift_forward\": \"latest\",\n        }.get(\n            nonexistent,  # type: ignore[arg-type]\n            None,\n        )\n        if nonexistent_pa is None:\n            raise NotImplementedError(f\"{nonexistent=} is not supported\")\n        if tz is None:\n            result = pc.local_timestamp(self._pa_array)\n        else:\n            result = pc.assume_timezone(\n                self._pa_array, str(tz), ambiguous=ambiguous, nonexistent=nonexistent_pa\n            )\n        return self._from_pyarrow_array(result)\n\n    def _dt_tz_convert(self, tz) -> Self:\n        if self.dtype.pyarrow_dtype.tz is None:\n            raise TypeError(\n                \"Cannot convert tz-naive timestamps, use tz_localize to localize\"\n            )\n        current_unit = self.dtype.pyarrow_dtype.unit\n        result = self._pa_array.cast(pa.timestamp(current_unit, tz))\n        return self._from_pyarrow_array(result)\n\n\ndef transpose_homogeneous_pyarrow(\n    arrays: Sequence[ArrowExtensionArray],\n) -> list[ArrowExtensionArray]:\n    \"\"\"Transpose arrow extension arrays in a list, but faster.\n\n    Input should be a list of arrays of equal length and all have the same\n    dtype. The caller is responsible for ensuring validity of input data.\n    \"\"\"\n    arrays = list(arrays)\n    nrows, ncols = len(arrays[0]), len(arrays)\n    indices = np.arange(nrows * ncols).reshape(ncols, nrows).T.reshape(-1)","sourceCodeStart":4289,"sourceCodeEnd":4325,"githubUrl":"https://github.com/pandas-dev/pandas/blob/3b7651241d4da534b3559b60ef128e1c34f54116/pandas/core/arrays/arrow/array.py#L4289-L4325","documentation":"Raised (as TypeError) by ArrowExtensionArray._dt_tz_convert when the array's pyarrow timestamp type has no timezone (tz is None). Converting timezones is only meaningful for tz-aware data; for tz-naive data you must first assign (localize to) a timezone. The message points the user at tz_localize.","triggerScenarios":"Calling `ser.dt.tz_convert('UTC')` on a Series whose dtype is `timestamp[ns][pyarrow]` with no timezone attached.","commonSituations":"Forgetting that tz_convert is the second step (after tz_localize); reading tz-naive arrow data and immediately trying to convert to UTC.","solutions":["Localize first: `ser.dt.tz_localize('UTC').dt.tz_convert('US/Eastern')`.","Inspect `ser.dtype.pyarrow_dtype.tz` — if None, you need tz_localize, not tz_convert."],"exampleFix":"# before\nser.dt.tz_convert('US/Eastern')      # ser is tz-naive\n# after\nser.dt.tz_localize('UTC').dt.tz_convert('US/Eastern')","handlingStrategy":"validation","validationCode":"if ser.dtype.pyarrow_dtype.tz is None:\n    raise TypeError(\"Series is tz-naive; call tz_localize before tz_convert\")\nser.dt.tz_convert(tz)","typeGuard":"def is_arrow_tz_aware(ser) -> bool:\n    t = getattr(ser.dtype, \"pyarrow_dtype\", None)\n    return t is not None and getattr(t, \"tz\", None) is not None","tryCatchPattern":"try:\n    out = ser.dt.tz_convert(tz)\nexcept TypeError as e:\n    if \"tz-naive\" in str(e):\n        out = ser.dt.tz_localize('UTC').dt.tz_convert(tz)\n    else:\n        raise","preventionTips":["Check pyarrow_dtype.tz before tz_convert","Localize naive timestamps before converting"],"tags":["pyarrow","datetime-accessor","timezone","type-error","arrow-extension"],"backgroundTag":null,"analyzedSha":"3b7651241d4da534b3559b60ef128e1c34f54116","analyzedAt":"2026-08-11T22:10:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}