{"record":{"id":"d2f272ff50aba5fa","repo":"pandas-dev/pandas","slug":"cannot-use-astype-to-convert-from-timezone-aware","errorCode":null,"errorMessage":"Cannot use .astype to convert from timezone-aware dtype to timezone-naive dtype. Use obj.tz_localize(None) or obj.tz_convert('UTC').tz_localize(None) instead.","messagePattern":"Cannot use \\.astype to convert from timezone-aware dtype to timezone-naive dtype\\. Use obj\\.tz_localize\\(None\\) or obj\\.tz_convert\\('UTC'\\)\\.tz_localize\\(None\\) instead\\.","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"pandas/core/arrays/datetimes.py","lineNumber":731,"sourceCode":"                res_values = astype_overflowsafe(self._ndarray, np_dtype, copy=copy)\n                return type(self)._simple_new(res_values, dtype=dtype)\n\n        elif (\n            self.tz is None\n            and lib.is_np_dtype(dtype, \"M\")\n            and not is_unitless(dtype)\n            and is_supported_dtype(dtype)\n        ):\n            # unit conversion e.g. datetime64[s]\n            res_values = astype_overflowsafe(self._ndarray, dtype, copy=True)\n            return type(self)._simple_new(res_values, dtype=res_values.dtype)\n            # TODO: preserve freq?\n\n        elif self.tz is not None and lib.is_np_dtype(dtype, \"M\"):\n            # pre-2.0 behavior for DTA/DTI was\n            #  values.tz_convert(\"UTC\").tz_localize(None), which did not match\n            #  the Series behavior\n            raise TypeError(\n                \"Cannot use .astype to convert from timezone-aware dtype to \"\n                \"timezone-naive dtype. Use obj.tz_localize(None) or \"\n                \"obj.tz_convert('UTC').tz_localize(None) instead.\"\n            )\n\n        elif (\n            self.tz is None\n            and lib.is_np_dtype(dtype, \"M\")\n            and dtype != self.dtype\n            and is_unitless(dtype)\n        ):\n            raise TypeError(\n                \"Casting to unit-less dtype 'datetime64' is not supported. \"\n                \"Pass e.g. 'datetime64[ns]' instead.\"\n            )\n\n        elif isinstance(dtype, PeriodDtype):\n            return self.to_period(freq=dtype.freq)","sourceCodeStart":713,"sourceCodeEnd":749,"githubUrl":"https://github.com/pandas-dev/pandas/blob/3b7651241d4da534b3559b60ef128e1c34f54116/pandas/core/arrays/datetimes.py#L713-L749","documentation":"Raised by DatetimeArray.astype (and DatetimeIndex.astype) when you call .astype() on a timezone-aware datetime array/index and request a plain numpy 'datetime64' (tz-naive) target dtype. astype cannot silently drop timezone information because that would be a lossy, ambiguous conversion. Pandas requires you to explicitly choose how to drop the tz via tz_localize(None) (keep wall time) or tz_convert('UTC').tz_localize(None) (keep UTC instant).","triggerScenarios":"Calling `tz_aware_dti.astype('datetime64[ns]')` or `tz_aware_series.astype('datetime64[ns]')` where the source has a DatetimeTZDtype (e.g. datetime64[ns, US/Eastern]) and the target dtype has no tz. Also triggered by `astype(np.dtype('M8[ns]'))` on a tz-aware DTA.","commonSituations":"Downstream code that stripped tz via astype in pandas <2.0 (the pre-2.0 behavior silently did tz_convert('UTC').tz_localize(None)); migrating to pandas 2.x without updating these calls. Passing data into libraries that dislike tz-aware columns (e.g. numpy-only ML pipelines) and reaching for astype out of habit.","solutions":["If you want to preserve the UTC instant: `obj.tz_convert('UTC').tz_localize(None)`.","If you want to keep the wall-clock values and just drop tz: `obj.tz_localize(None)`.","If you wanted to change units on a tz-aware array, pass a DatetimeTZDtype target like `obj.astype('datetime64[s, US/Eastern]')` instead of a tz-naive one.","For DataFrame/Series columns, operate via `.dt` accessor: `s.dt.tz_localize(None)` or `s.dt.tz_convert('UTC').dt.tz_localize(None)`."],"exampleFix":"// before\ndf['ts'] = df['ts'].astype('datetime64[ns]')  # ts is tz-aware\n\n// after\ndf['ts'] = df['ts'].dt.tz_convert('UTC').dt.tz_localize(None)","handlingStrategy":"validation","validationCode":"import pandas as pd\n\ndef to_tz_naive(obj):\n    if getattr(obj.dtype, 'tz', None) is not None:\n        return obj.dt.tz_convert('UTC').dt.tz_localize(None)\n    return obj","typeGuard":"def is_tz_aware(obj) -> bool:\n    dt = getattr(obj, 'dtype', None)\n    return getattr(dt, 'tz', None) is not None","tryCatchPattern":"try:\n    out = col.astype('datetime64[ns]')\nexcept TypeError as e:\n    if 'timezone-aware dtype to timezone-naive' in str(e):\n        out = col.dt.tz_convert('UTC').dt.tz_localize(None)\n    else:\n        raise","preventionTips":["Audit every `.astype('datetime64...')` call against tz-aware data when migrating to pandas 2.x.","Centralize tz handling in one helper rather than dropping tz inline at multiple call sites.","Prefer the `.dt.tz_*` accessors over astype for any tz operation."],"tags":["datetime","timezone","astype","dtype","migration"],"backgroundTag":null,"analyzedSha":"3b7651241d4da534b3559b60ef128e1c34f54116","analyzedAt":"2026-08-11T22:10:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}