{"record":{"id":"1483e41519e3be0a","repo":"pandas-dev/pandas","slug":"self-dtype-does-not-have-duration-components","errorCode":null,"errorMessage":"'{self.dtype}' does not have duration components","messagePattern":"'(.+?)' does not have duration components","errorType":"exception","errorClass":"AttributeError","httpStatus":null,"severity":"error","filePath":"pandas/core/arrays/arrow/array.py","lineNumber":3830,"sourceCode":"                pa.scalar(None, type=pa.int32()),\n                pa.scalar(0, type=pa.int32()),\n            )\n        )\n\n    @property\n    def _duration_unit(self) -> str:\n        \"\"\"\n        Return the time unit for a duration-typed array.\n\n        Raises ``AttributeError`` for non-duration dtypes so the duration\n        component accessors (``_dt_days``, ``_dt_seconds``, etc.) are rejected\n        for e.g. ``timestamp[pyarrow]`` instead of silently treating the\n        underlying int64 as a duration. The ``dt`` dispatcher turns this into\n        the usual \"dt.<name> is not supported for <dtype>\" error.\n        \"\"\"\n        pa_type = self._pa_array.type\n        if not pa.types.is_duration(pa_type):\n            raise AttributeError(f\"'{self.dtype}' does not have duration components\")\n        return pa_type.unit\n\n    @cache_readonly\n    def _dt_day_remainder(self) -> pa.ChunkedArray:\n        \"\"\"\n        Return the remainder after removing full days, always non-negative.\n\n        For negative durations like -22h 57m 57s (= -1 day + 1h 2m 3s),\n        this returns the positive offset from the day boundary.\n\n        This is cached because it's used by all sub-day component accessors.\n        \"\"\"\n        unit = self._duration_unit\n        divisor = _DURATION_DIVISORS[\"day\"][unit]\n        arr = self._pa_array.cast(pa.int64())\n        days = floor_div_int64(arr, divisor)\n        # remainder = arr - days * divisor (always non-negative)\n        return pc.subtract(arr, pc.multiply(days, divisor))","sourceCodeStart":3812,"sourceCodeEnd":3848,"githubUrl":"https://github.com/pandas-dev/pandas/blob/3b7651241d4da534b3559b60ef128e1c34f54116/pandas/core/arrays/arrow/array.py#L3812-L3848","documentation":"Raised by ArrowExtensionArray._duration_unit (an AttributeError, not ValueError) when the array's pyarrow type is not a `duration` type. The duration-component accessors (_dt_day_remainder, _dt_days, _dt_seconds, etc.) depend on it; by raising AttributeError the `.dt` dispatcher converts this into the standard 'dt.<name> is not supported for <dtype>' message, preventing the underlying int64 from being silently interpreted as a duration.","triggerScenarios":"Calling a duration-only component like `ser.dt.days`, `ser.dt.seconds`, or `ser.dt.microseconds` on a Series whose dtype is a pyarrow timestamp (timestamp[pyarrow]) or date rather than a duration[pyarrow].","commonSituations":"Subtracting two timestamps produces a duration, but operating directly on a timestamp column with `.dt.days`; confusing timedelta semantics with timestamp semantics on arrow dtypes.","solutions":["Ensure the Series actually holds durations: compute a difference first, e.g. `ser = end_ts - start_ts`, so the dtype becomes duration[pyarrow].","If you wanted calendar components of a timestamp, use the timestamp-specific accessors (`.dt.day`, `.dt.hour`) instead of `.dt.days`."],"exampleFix":"# before\nts.dt.days            # ts is timestamp[pyarrow]\n# after\n(ts - ts.iloc[0]).dt.days   # duration[pyarrow]","handlingStrategy":"validation","validationCode":"import pyarrow as pa\npa_type = ser.dtype.pyarrow_dtype\nif not pa.types.is_duration(pa_type):\n    raise AttributeError(f\"{ser.dtype} has no duration components; compute a delta first\")\nser.dt.days","typeGuard":"def is_arrow_duration(ser) -> bool:\n    import pyarrow as pa\n    return hasattr(ser.dtype, \"pyarrow_dtype\") and pa.types.is_duration(ser.dtype.pyarrow_dtype)","tryCatchPattern":"try:\n    out = ser.dt.days\nexcept AttributeError:\n    out = (ser - ser.iloc[0]).dt.days   # construct a duration","preventionTips":["Confirm duration dtype before using .dt.days/seconds","Subtract two timestamps to obtain a duration when you need components"],"tags":["pyarrow","datetime-accessor","duration","arrow-extension","attribute-error"],"backgroundTag":null,"analyzedSha":"3b7651241d4da534b3559b60ef128e1c34f54116","analyzedAt":"2026-08-11T22:10:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}