Skip to content

Commit 1c71d7d

Browse files
authored
BUG: Series[object].astype(td64_unsupported) (#50070)
1 parent dba2080 commit 1c71d7d

File tree

3 files changed

+44
-9
lines changed

3 files changed

+44
-9
lines changed

pandas/core/dtypes/astype.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import inspect
88
from typing import (
99
TYPE_CHECKING,
10+
cast,
1011
overload,
1112
)
1213

@@ -36,7 +37,11 @@
3637
from pandas.core.dtypes.missing import isna
3738

3839
if TYPE_CHECKING:
39-
from pandas.core.arrays import ExtensionArray
40+
from pandas.core.arrays import (
41+
DatetimeArray,
42+
ExtensionArray,
43+
TimedeltaArray,
44+
)
4045

4146

4247
_dtype_obj = np.dtype(object)
@@ -109,7 +114,11 @@ def astype_nansafe(
109114

110115
# allow frequency conversions
111116
if dtype.kind == "M":
112-
return arr.astype(dtype)
117+
from pandas.core.construction import ensure_wrapped_if_datetimelike
118+
119+
dta = ensure_wrapped_if_datetimelike(arr)
120+
dta = cast("DatetimeArray", dta)
121+
return dta.astype(dtype, copy=copy)._ndarray
113122

114123
raise TypeError(f"cannot astype a datetimelike from [{arr.dtype}] to [{dtype}]")
115124

@@ -124,8 +133,9 @@ def astype_nansafe(
124133
# and doing the old convert-to-float behavior otherwise.
125134
from pandas.core.construction import ensure_wrapped_if_datetimelike
126135

127-
arr = ensure_wrapped_if_datetimelike(arr)
128-
return arr.astype(dtype, copy=copy)
136+
tda = ensure_wrapped_if_datetimelike(arr)
137+
tda = cast("TimedeltaArray", tda)
138+
return tda.astype(dtype, copy=copy)._ndarray
129139

130140
raise TypeError(f"cannot astype a timedelta from [{arr.dtype}] to [{dtype}]")
131141

@@ -145,10 +155,15 @@ def astype_nansafe(
145155
return dta.astype(dtype, copy=False)._ndarray
146156

147157
elif is_timedelta64_dtype(dtype):
158+
from pandas.core.construction import ensure_wrapped_if_datetimelike
159+
148160
# bc we know arr.dtype == object, this is equivalent to
149161
# `np.asarray(to_timedelta(arr))`, but using a lower-level API that
150162
# does not require a circular import.
151-
return array_to_timedelta64(arr).view("m8[ns]").astype(dtype, copy=False)
163+
tdvals = array_to_timedelta64(arr).view("m8[ns]")
164+
165+
tda = ensure_wrapped_if_datetimelike(tdvals)
166+
return tda.astype(dtype, copy=False)._ndarray
152167

153168
if dtype.name in ("datetime64", "timedelta64"):
154169
msg = (

pandas/tests/frame/methods/test_astype.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,22 @@ def test_astype_from_object_to_datetime_unit(self, unit):
387387
with pytest.raises(TypeError, match="Cannot cast"):
388388
df.astype(f"M8[{unit}]")
389389

390+
@pytest.mark.parametrize("unit", ["Y", "M", "W", "D", "h", "m"])
391+
def test_astype_from_object_to_timedelta_unit(self, unit):
392+
vals = [
393+
["1 Day", "2 Days", "3 Days"],
394+
["4 Days", "5 Days", "6 Days"],
395+
]
396+
df = DataFrame(vals, dtype=object)
397+
msg = (
398+
r"Cannot convert from timedelta64\[ns\] to timedelta64\[.*\]. "
399+
"Supported resolutions are 's', 'ms', 'us', 'ns'"
400+
)
401+
with pytest.raises(ValueError, match=msg):
402+
# TODO: this is ValueError while for DatetimeArray it is TypeError;
403+
# get these consistent
404+
df.astype(f"m8[{unit}]")
405+
390406
@pytest.mark.parametrize("dtype", ["M8", "m8"])
391407
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s", "h", "m", "D"])
392408
def test_astype_from_datetimelike_to_object(self, dtype, unit):

pandas/tests/reshape/merge/test_merge.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -757,14 +757,18 @@ def test_other_datetime_unit(self, unit):
757757
def test_other_timedelta_unit(self, unit):
758758
# GH 13389
759759
df1 = DataFrame({"entity_id": [101, 102]})
760-
s = Series([None, None], index=[101, 102], name="days")
760+
ser = Series([None, None], index=[101, 102], name="days")
761761

762762
dtype = f"m8[{unit}]"
763-
df2 = s.astype(dtype).to_frame("days")
764763
if unit in ["D", "h", "m"]:
765-
# We get nearest supported unit, i.e. "s"
766-
assert df2["days"].dtype == "m8[s]"
764+
# We cannot astype, instead do nearest supported unit, i.e. "s"
765+
msg = "Supported resolutions are 's', 'ms', 'us', 'ns'"
766+
with pytest.raises(ValueError, match=msg):
767+
ser.astype(dtype)
768+
769+
df2 = ser.astype("m8[s]").to_frame("days")
767770
else:
771+
df2 = ser.astype(dtype).to_frame("days")
768772
assert df2["days"].dtype == dtype
769773

770774
result = df1.merge(df2, left_on="entity_id", right_index=True)

0 commit comments

Comments
 (0)