Skip to content

Commit 100dd37

Browse files
authored
REF: move dtype-validation out of maybe_cast_to_datetime (#40190)
1 parent c500987 commit 100dd37

File tree

1 file changed

+53
-42
lines changed

1 file changed

+53
-42
lines changed

pandas/core/dtypes/cast.py

+53-42
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
Timedelta,
3939
Timestamp,
4040
conversion,
41-
iNaT,
4241
ints_to_pydatetime,
4342
)
4443
from pandas._libs.tslibs.timedeltas import array_to_timedelta64
@@ -1613,50 +1612,16 @@ def maybe_cast_to_datetime(
16131612
is_datetime64tz = is_datetime64tz_dtype(dtype)
16141613
is_timedelta64 = is_timedelta64_dtype(dtype)
16151614

1616-
if is_datetime64 or is_datetime64tz or is_timedelta64:
1617-
1618-
# Force the dtype if needed.
1619-
msg = (
1620-
f"The '{dtype.name}' dtype has no unit. "
1621-
f"Please pass in '{dtype.name}[ns]' instead."
1622-
)
1615+
vdtype = getattr(value, "dtype", None)
16231616

1624-
if is_datetime64:
1625-
# unpack e.g. SparseDtype
1626-
dtype = getattr(dtype, "subtype", dtype)
1627-
if not is_dtype_equal(dtype, DT64NS_DTYPE):
1628-
1629-
# pandas supports dtype whose granularity is less than [ns]
1630-
# e.g., [ps], [fs], [as]
1631-
if dtype <= np.dtype("M8[ns]"):
1632-
if dtype.name == "datetime64":
1633-
raise ValueError(msg)
1634-
dtype = DT64NS_DTYPE
1635-
else:
1636-
raise TypeError(
1637-
f"cannot convert datetimelike to dtype [{dtype}]"
1638-
)
1639-
1640-
elif is_timedelta64 and not is_dtype_equal(dtype, TD64NS_DTYPE):
1641-
1642-
# pandas supports dtype whose granularity is less than [ns]
1643-
# e.g., [ps], [fs], [as]
1644-
if dtype <= np.dtype("m8[ns]"):
1645-
if dtype.name == "timedelta64":
1646-
raise ValueError(msg)
1647-
dtype = TD64NS_DTYPE
1648-
else:
1649-
raise TypeError(f"cannot convert timedeltalike to dtype [{dtype}]")
1617+
if is_datetime64 or is_datetime64tz or is_timedelta64:
1618+
dtype = ensure_nanosecond_dtype(dtype)
16501619

16511620
if not is_sparse(value):
16521621
value = np.array(value, copy=False)
16531622

1654-
# have a scalar array-like (e.g. NaT)
1655-
if value.ndim == 0:
1656-
value = iNaT
1657-
16581623
# we have an array of datetime or timedeltas & nulls
1659-
elif value.size or not is_dtype_equal(value.dtype, dtype):
1624+
if value.size or not is_dtype_equal(value.dtype, dtype):
16601625
_disallow_mismatched_datetimelike(value, dtype)
16611626

16621627
try:
@@ -1665,6 +1630,8 @@ def maybe_cast_to_datetime(
16651630
# GH 25843: Remove tz information since the dtype
16661631
# didn't specify one
16671632
if dta.tz is not None:
1633+
# equiv: dta.view(dtype)
1634+
# Note: NOT equivalent to dta.astype(dtype)
16681635
dta = dta.tz_localize(None)
16691636
value = dta
16701637
elif is_datetime64tz:
@@ -1678,10 +1645,12 @@ def maybe_cast_to_datetime(
16781645
value = dta.astype(dtype, copy=False)
16791646
elif is_dt_string:
16801647
# Strings here are naive, so directly localize
1648+
# equiv: dta.astype(dtype) # though deprecated
16811649
value = dta.tz_localize(dtype.tz)
16821650
else:
16831651
# Numeric values are UTC at this point,
16841652
# so localize and convert
1653+
# equiv: Series(dta).astype(dtype) # though deprecated
16851654
value = dta.tz_localize("UTC").tz_convert(dtype.tz)
16861655
elif is_timedelta64:
16871656
# if successful, we get a ndarray[td64ns]
@@ -1694,9 +1663,7 @@ def maybe_cast_to_datetime(
16941663
pass
16951664

16961665
# coerce datetimelike to object
1697-
elif is_datetime64_dtype(
1698-
getattr(value, "dtype", None)
1699-
) and not is_datetime64_dtype(dtype):
1666+
elif is_datetime64_dtype(vdtype) and not is_datetime64_dtype(dtype):
17001667
if is_object_dtype(dtype):
17011668
value = cast(np.ndarray, value)
17021669

@@ -1740,6 +1707,50 @@ def sanitize_to_nanoseconds(values: np.ndarray) -> np.ndarray:
17401707
return values
17411708

17421709

1710+
def ensure_nanosecond_dtype(dtype: DtypeObj) -> DtypeObj:
1711+
"""
1712+
Convert dtypes with granularity less than nanosecond to nanosecond
1713+
1714+
>>> ensure_nanosecond_dtype(np.dtype("M8[s]"))
1715+
dtype('<M8[ns]')
1716+
1717+
>>> ensure_nanosecond_dtype(np.dtype("m8[ps]"))
1718+
TypeError: cannot convert timedeltalike to dtype [timedelta64[ps]]
1719+
"""
1720+
msg = (
1721+
f"The '{dtype.name}' dtype has no unit. "
1722+
f"Please pass in '{dtype.name}[ns]' instead."
1723+
)
1724+
1725+
# unpack e.g. SparseDtype
1726+
dtype = getattr(dtype, "subtype", dtype)
1727+
1728+
if not isinstance(dtype, np.dtype):
1729+
# i.e. datetime64tz
1730+
pass
1731+
1732+
elif dtype.kind == "M" and dtype != DT64NS_DTYPE:
1733+
# pandas supports dtype whose granularity is less than [ns]
1734+
# e.g., [ps], [fs], [as]
1735+
if dtype <= np.dtype("M8[ns]"):
1736+
if dtype.name == "datetime64":
1737+
raise ValueError(msg)
1738+
dtype = DT64NS_DTYPE
1739+
else:
1740+
raise TypeError(f"cannot convert datetimelike to dtype [{dtype}]")
1741+
1742+
elif dtype.kind == "m" and dtype != TD64NS_DTYPE:
1743+
# pandas supports dtype whose granularity is less than [ns]
1744+
# e.g., [ps], [fs], [as]
1745+
if dtype <= np.dtype("m8[ns]"):
1746+
if dtype.name == "timedelta64":
1747+
raise ValueError(msg)
1748+
dtype = TD64NS_DTYPE
1749+
else:
1750+
raise TypeError(f"cannot convert timedeltalike to dtype [{dtype}]")
1751+
return dtype
1752+
1753+
17431754
def find_common_type(types: List[DtypeObj]) -> DtypeObj:
17441755
"""
17451756
Find a common data type among the given dtypes.

0 commit comments

Comments
 (0)