pandas-dev/pandas · error · AttributeError
'{self.dtype}' does not have duration components
Error message
'{self.dtype}' does not have duration components What it means
Raised by the `_duration_unit` property of ArrowExtensionArray when the underlying pyarrow type is not a `pyarrow.duration(...)` type. The duration-component accessors (_dt_days, _dt_hours, _dt_seconds, etc.) call _duration_unit to obtain the unit, so calling them on a non-duration array (e.g. timestamp[pyarrow]) raises AttributeError. The dt dispatcher converts this into the standard 'dt.<name> is not supported for <dtype>' message.
Source
Thrown at pandas/core/arrays/arrow/array.py:3802
pa.scalar(None, type=pa.int32()),
pa.scalar(0, type=pa.int32()),
)
)
@property
def _duration_unit(self) -> str:
"""
Return the time unit for a duration-typed array.
Raises ``AttributeError`` for non-duration dtypes so the duration
component accessors (``_dt_days``, ``_dt_seconds``, etc.) are rejected
for e.g. ``timestamp[pyarrow]`` instead of silently treating the
underlying int64 as a duration. The ``dt`` dispatcher turns this into
the usual "dt.<name> is not supported for <dtype>" error.
"""
pa_type = self._pa_array.type
if not pa.types.is_duration(pa_type):
raise AttributeError(f"'{self.dtype}' does not have duration components")
return pa_type.unit
@cache_readonly
def _dt_day_remainder(self) -> pa.ChunkedArray:
"""
Return the remainder after removing full days, always non-negative.
For negative durations like -22h 57m 57s (= -1 day + 1h 2m 3s),
this returns the positive offset from the day boundary.
This is cached because it's used by all sub-day component accessors.
"""
unit = self._duration_unit
divisor = _DURATION_DIVISORS["day"][unit]
arr = self._pa_array.cast(pa.int64())
days = floor_div_int64(arr, divisor)
# remainder = arr - days * divisor (always non-negative)
return pc.subtract(arr, pc.multiply(days, divisor))View on GitHub (pinned to 71959b8cb9)
Solutions
- Ensure the Series is a duration type: subtract two timestamps to get a duration, or cast: `s.astype("duration[s][pyarrow]")` (if semantically valid).
- Use the correct accessor for the dtype: timestamps use `s.dt.day`, `s.dt.hour`, not `s.dt.days`.
- Convert to timedelta64[ns] then use timedelta accessors: `s.astype("timedelta64[ns]").dt.days`.
- Inspect dtype first: `print(s.dtype)` and switch the accessor accordingly.
Example fix
# before
s = pd.Series(pd.to_datetime(["2024-01-01","2024-01-02"]), dtype="timestamp[us][pyarrow]")
s.dt.days # AttributeError
# after (compute a duration first)
dur = s - s.iloc[0]
dur.astype("duration[us][pyarrow]").dt.days Defensive patterns
Strategy: type-guard
Validate before calling
import pyarrow as pa
def is_pyarrow_duration(s) -> bool:
try:
return pa.types.is_duration(s.dtype.pyarrow_dtype)
except AttributeError:
return False
def safe_duration_component(s, name):
if not is_pyarrow_duration(s):
raise AttributeError(f"{s.dtype} has no duration components; use a duration[pyarrow] dtype")
return getattr(s.dt, name) Type guard
import pyarrow as pa
def is_duration_series(s) -> bool:
pa_dt = getattr(s.dtype, "pyarrow_dtype", None)
return pa_dt is not None and pa.types.is_duration(pa_dt) Try / catch
try:
days = s.dt.days
except AttributeError:
# not a duration; recompute as duration from timestamps if applicable
days = (s - s.iloc[0]).astype("duration[us][pyarrow]").dt.days Prevention
- Subtract two timestamps to materialize a duration before using .dt.days/.seconds.
- Check s.dtype and pa.types.is_duration before duration accessors.
- Keep date vs timestamp vs duration dtypes explicit in schemas.
When it happens
Trigger: Calling `s.dt.days`, `s.dt.seconds`, `s.dt.microseconds`, `s.dt.components`, etc. on a Series whose dtype is `timestamp[pyarrow]`, `date32[pyarrow]`, or any non-duration pyarrow type. Confusing timedelta semantics (which support .dt.days) with timestamp semantics.
Common situations: Subtracting two timestamp Series and forgetting to keep the result as a duration; pandas may materialize it differently. Applying generic timedelta-style accessors to a column that was loaded from Parquet/Arrow as a timestamp.
Related errors
- to_pydatetime cannot be called with {self.dtype.pyarrow_dtyp
- as_unit not implemented for {pa_type}
- ambiguous is not supported.
- nonexistent is not supported.
- Must specify a valid frequency: {freq}
AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07).
Data as JSON: /api/errors/1483e41519e3be0a.
Report an issue: GitHub.