@@ -19,6 +19,7 @@ from cpython.object cimport (
19
19
20
20
import_datetime()
21
21
22
+ import numpy as np
22
23
cimport numpy as cnp
23
24
24
25
cnp.import_array()
@@ -288,13 +289,21 @@ cpdef ndarray astype_overflowsafe(
288
289
bint copy = True ,
289
290
):
290
291
"""
291
- Convert an ndarray with datetime64[X] to datetime64[Y], raising on overflow.
292
+ Convert an ndarray with datetime64[X] to datetime64[Y]
293
+ or timedelta64[X] to timedelta64[Y],
294
+ raising on overflow.
292
295
"""
293
- if values.descr.type_num != cnp.NPY_DATETIME:
294
- # aka values.dtype.kind != "M"
295
- raise TypeError (" astype_overflowsafe values must have datetime64 dtype" )
296
- if dtype.type_num != cnp.NPY_DATETIME:
297
- raise TypeError (" astype_overflowsafe dtype must be datetime64" )
296
+ if values.descr.type_num == dtype.type_num == cnp.NPY_DATETIME:
297
+ # i.e. dtype.kind == "M"
298
+ pass
299
+ elif values.descr.type_num == dtype.type_num == cnp.NPY_TIMEDELTA:
300
+ # i.e. dtype.kind == "m"
301
+ pass
302
+ else :
303
+ raise TypeError (
304
+ " astype_overflowsafe values.dtype and dtype must be either "
305
+ " both-datetime64 or both-timedelta64."
306
+ )
298
307
299
308
cdef:
300
309
NPY_DATETIMEUNIT from_unit = get_unit_from_dtype(values.dtype)
@@ -306,14 +315,21 @@ cpdef ndarray astype_overflowsafe(
306
315
):
307
316
# without raising explicitly here, we end up with a SystemError
308
317
# built-in function [...] returned a result with an error
309
- raise ValueError (" datetime64 values and dtype must have a unit specified" )
318
+ raise ValueError (
319
+ " datetime64/timedelta64 values and dtype must have a unit specified"
320
+ )
310
321
311
322
if from_unit == to_unit:
312
323
# Check this before allocating result for perf, might save some memory
313
324
if copy:
314
325
return values.copy()
315
326
return values
316
327
328
+ elif from_unit > to_unit:
329
+ # e.g. ns -> us, so there is no risk of overflow, so we can use
330
+ # numpy's astype safely. Note there _is_ risk of truncation.
331
+ return values.astype(dtype)
332
+
317
333
cdef:
318
334
ndarray i8values = values.view(" i8" )
319
335
@@ -326,6 +342,7 @@ cpdef ndarray astype_overflowsafe(
326
342
Py_ssize_t i, N = values.size
327
343
int64_t value, new_value
328
344
npy_datetimestruct dts
345
+ bint is_td = dtype.type_num == cnp.NPY_TIMEDELTA
329
346
330
347
for i in range (N):
331
348
# Analogous to: item = values[i]
@@ -335,7 +352,20 @@ cpdef ndarray astype_overflowsafe(
335
352
new_value = NPY_DATETIME_NAT
336
353
else :
337
354
pandas_datetime_to_datetimestruct(value, from_unit, & dts)
338
- check_dts_bounds(& dts, to_unit)
355
+
356
+ try :
357
+ check_dts_bounds(& dts, to_unit)
358
+ except OutOfBoundsDatetime as err:
359
+ if is_td:
360
+ tdval = np.timedelta64(value).view(values.dtype)
361
+ msg = (
362
+ " Cannot convert {tdval} to {dtype} without overflow"
363
+ .format(tdval = str (tdval), dtype = str (dtype))
364
+ )
365
+ raise OutOfBoundsTimedelta(msg) from err
366
+ else :
367
+ raise
368
+
339
369
new_value = npy_datetimestruct_to_datetime(to_unit, & dts)
340
370
341
371
# Analogous to: iresult[i] = new_value
0 commit comments