pandas-dev/pandas · error · NotImplementedError

as_unit not implemented for

Error message

as_unit not implemented for {pa_type}

What it means

Raised by ArrowExtensionArray._dt_as_unit when the array's pyarrow type is neither a timestamp nor a duration. `_dt_as_unit` reinterprets the value into a different unit (s/ms/us/ns) and only pyarrow timestamp and duration types carry a `unit`; any other type (date, time, int, etc.) is unsupported.

Solutions

  1. Cast the column to a pyarrow timestamp first: `ser.astype('timestamp[ms][pyarrow]')` then call `.dt.as_unit`.
  2. Confirm the dtype with `ser.dtype` before calling `.dt.as_unit`; only proceed for timestamp/duration arrow dtypes.

Example fix

# before
date_col.dt.as_unit('ms')      # date32[pyarrow]
# after
date_col.astype('timestamp[ms][pyarrow]').dt.as_unit('ms')
Defensive patterns

Strategy: validation

Validate before calling

import pyarrow as pa
t = ser.dtype.pyarrow_dtype
if not (pa.types.is_timestamp(t) or pa.types.is_duration(t)):
    raise NotImplementedError(f"as_unit not supported for {t}; cast to timestamp first")
ser.dt.as_unit('ms')

Type guard

def supports_as_unit(ser) -> bool:
    import pyarrow as pa
    t = getattr(ser.dtype, "pyarrow_dtype", None)
    return t is not None and (pa.types.is_timestamp(t) or pa.types.is_duration(t))

Try / catch

try:
    out = ser.dt.as_unit('ms')
except NotImplementedError as e:
    if "as_unit not implemented" in str(e):
        out = ser.astype('timestamp[ms][pyarrow]')
    else:
        raise

Prevention

When it happens

Trigger: Calling `ser.dt.as_unit('ms')` (via `.dt.as_unit`) on a pyarrow-backed Series whose dtype is not timestamp[pyarrow] or duration[pyarrow] — for example a date32[pyarrow] or time32[pyarrow] column.

Common situations: Calling `.dt.as_unit` on a date-typed arrow column expecting it to act like a timestamp; chaining after an operation that changed the dtype away from timestamp/duration.

Related errors


AI-assisted analysis of pandas-dev/pandas@3b7651241d (2026-08-11). Data as JSON: /api/errors/913b6bed746dcdf7. Report an issue: GitHub.

Appendix: source

Thrown at pandas/core/arrays/arrow/array.py:3960

        data = self._pa_array.to_pylist()
        if self._dtype.pyarrow_dtype.unit == "ns":
            data = [None if ts is None else ts.to_pytimedelta() for ts in data]
        return np.array(data, dtype=object)

    def _dt_total_seconds(self) -> Self:
        unit = self._pa_array.type.unit
        unit_per_second = {"s": 1.0, "ms": 1e3, "us": 1e6, "ns": 1e9}
        result = pc.divide(pc.cast(self._pa_array, pa.int64()), unit_per_second[unit])
        return self._from_pyarrow_array(result)

    def _dt_as_unit(self, unit: str) -> Self:
        pa_type = self._pa_array.type
        if pa.types.is_timestamp(pa_type):
            target_type = pa.timestamp(unit, tz=pa_type.tz)
        elif pa.types.is_duration(pa_type):
            target_type = pa.duration(unit)
        else:
            raise NotImplementedError(f"as_unit not implemented for {pa_type}")

        nanos_per_unit = {"s": 1_000_000_000, "ms": 1_000_000, "us": 1_000, "ns": 1}
        from_nanos = nanos_per_unit[pa_type.unit]
        to_nanos = nanos_per_unit[unit]
        if to_nanos <= from_nanos:
            # Same or finer resolution: exact upscale. Use safe=True so that
            # out-of-bounds values raise instead of silently wrapping, matching
            # numpy/pandas as_unit.
            try:
                result = pc.cast(self._pa_array, target_type)
            except pa.ArrowInvalid as err:
                err_type = (
                    OutOfBoundsDatetime
                    if pa.types.is_timestamp(pa_type)
                    else OutOfBoundsTimedelta
                )
                raise err_type(
                    f"Cannot convert {pa_type} to {target_type} without overflow"

View on GitHub (pinned to 3b7651241d)