Repository: pandas-dev/pandas

Issue:
BUG: to_pydatetime() -> 'pyarrow.lib.DataType' object has no attribute 'unit'
### Pandas version checks

- [X] I have checked that this issue has not already been reported.

- [X] I have confirmed this bug exists on the [latest version](https://pandas.pydata.org/docs/whatsnew/index.html) of pandas.

- [ ] I have confirmed this bug exists on the [main branch](https://pandas.pydata.org/docs/dev/getting_started/install.html#installing-the-development-version-of-pandas) of pandas.


### Reproducible Example

```python
import polars as pl
import datetime

pl.Series([datetime.date(2022,12,31)]).to_pandas(use_pyarrow_extension_array=True).dt.to_pydatetime()

"""AttributeError: 'pyarrow.lib.DataType' object has no attribute 'unit'"""
```


### Issue Description

For context, I was feeding a pandas DataFrame with pyarrow dtypes into the `dash` package, which called `s.dt.to_pydatetime()` on a date series `s`. Maybe the problem is dash's fault, but this seems like a pandas bug.

### Expected Behavior

Return datetime objects as expected.

### Installed Versions

<details>

ModuleNotFoundError: No module named 'PySide6'

</details>



Discussion:
Can you post the pull traceback?
```python
~/.local/lib/python3.9/site-packages/pandas/core/indexes/accessors.py in to_pydatetime(self)
    211 
    212     def to_pydatetime(self):
--> 213         return cast(ArrowExtensionArray, self._parent.array)._dt_to_pydatetime()
    214 
    215     def isocalendar(self):

~/.local/lib/python3.9/site-packages/pandas/core/arrays/arrow/array.py in _dt_to_pydatetime(self)
   2085     def _dt_to_pydatetime(self):
   2086         data = self._data.to_pylist()
-> 2087         if self._dtype.pyarrow_dtype.unit == "ns":
   2088             data = [None if ts is None else ts.to_pydatetime(warn=False) for ts in data]
   2089         return np.array(data, dtype=object)

AttributeError: 'pyarrow.lib.DataType' object has no attribute 'unit'
```
Okay here's a pandas reproducer

```
In [1]: import datetime

In [2]: import numpy as np; import pandas as pd; import pyarrow as pa

In [3]: pd.Series(pd.arrays.ArrowExtensionArray(pa.array([datetime.date.today()]))).dt.to_pydatetime()
AttributeError: 'pyarrow.lib.DataType' object has no attribute 'unit'
```

Not 100% that this should work, `datetime.date` and `datetime.datetime` are different objects. Probably should raise a better error message here that the pyarrow type should be `pa.timestamp` type specifically
isnt this why we have `get_unit_from_pa_dtype`?
> get_unit_from_pa_dtype

Looks specifically used for `pa.time` types, but for a totally different application.

The issue here is `date[unit][pyarrow]` (and not necessarily `timestamp[unit][pyarrow]`) type working with a `to_pydatetime()` method
Labelling 2.0.1 for now, think it might make sense back porting a better error message

Patch:
diff --git a/doc/source/whatsnew/v2.0.2.rst b/doc/source/whatsnew/v2.0.2.rst
index adfebd857b390..3ee7031795d16 100644
--- a/doc/source/whatsnew/v2.0.2.rst
+++ b/doc/source/whatsnew/v2.0.2.rst
@@ -31,7 +31,7 @@ Bug fixes
 
 Other
 ~~~~~
--
+- Raised a better error message when calling :func:`Series.dt.to_pydatetime` with :class:`ArrowDtype` with ``pyarrow.date32`` or ``pyarrow.date64`` type (:issue:`52812`)
 
 .. ---------------------------------------------------------------------------
 .. _whatsnew_202.contributors:
diff --git a/pandas/core/arrays/arrow/array.py b/pandas/core/arrays/arrow/array.py
index dfba9e4a02af0..8d76b0910814c 100644
--- a/pandas/core/arrays/arrow/array.py
+++ b/pandas/core/arrays/arrow/array.py
@@ -2344,6 +2344,11 @@ def _dt_month_name(self, locale: str | None = None):
         return type(self)(pc.strftime(self._pa_array, format="%B", locale=locale))
 
     def _dt_to_pydatetime(self):
+        if pa.types.is_date(self.dtype.pyarrow_dtype):
+            raise ValueError(
+                f"to_pydatetime cannot be called with {self.dtype.pyarrow_dtype} type. "
+                "Convert to pyarrow timestamp type."
+            )
         data = self._pa_array.to_pylist()
         if self._dtype.pyarrow_dtype.unit == "ns":
             data = [None if ts is None else ts.to_pydatetime(warn=False) for ts in data]


Test patch:
diff --git a/pandas/tests/extension/test_arrow.py b/pandas/tests/extension/test_arrow.py
index 4caa982fa7b64..e4115e235cd6b 100644
--- a/pandas/tests/extension/test_arrow.py
+++ b/pandas/tests/extension/test_arrow.py
@@ -2615,6 +2615,19 @@ def test_dt_to_pydatetime():
     tm.assert_numpy_array_equal(result, expected)
 
 
+@pytest.mark.parametrize("date_type", [32, 64])
+def test_dt_to_pydatetime_date_error(date_type):
+    # GH 52812
+    ser = pd.Series(
+        [date(2022, 12, 31)],
+        dtype=ArrowDtype(getattr(pa, f"date{date_type}")()),
+    )
+    msg = "The behavior of ArrowTemporalProperties.to_pydatetime is deprecated"
+    with tm.assert_produces_warning(FutureWarning, match=msg):
+        with pytest.raises(ValueError, match="to_pydatetime cannot be called with"):
+            ser.dt.to_pydatetime()
+
+
 def test_dt_tz_localize_unsupported_tz_options():
     ser = pd.Series(
         [datetime(year=2023, month=1, day=2, hour=3), None],