@@ -333,58 +333,39 @@ cdef convert_to_timedelta64(object ts, str unit):
333333
334334 Handle these types of objects:
335335 - timedelta/Timedelta
336- - timedelta64
337- - an offset
338- - np.int64 (with unit providing a possible modifier)
339- - None/NaT
340336
341- Return an ns based int64
337+ Return an timedelta64[ns] object
342338 """
343339 # Caller is responsible for checking unit not in ["Y", "y", "M"]
344- if checknull_with_nat_and_na(ts):
345- return np.timedelta64(NPY_NAT, " ns" )
346- elif isinstance (ts, _Timedelta):
340+ if isinstance (ts, _Timedelta):
347341 # already in the proper format
348342 if ts._creso != NPY_FR_ns:
349343 ts = ts.as_unit(" ns" ).asm8
350344 else :
351345 ts = np.timedelta64(ts._value, " ns" )
352- elif cnp.is_timedelta64_object(ts):
353- ts = ensure_td64ns(ts)
354- elif is_integer_object(ts):
355- if ts == NPY_NAT:
356- return np.timedelta64(NPY_NAT, " ns" )
357- else :
358- ts = _maybe_cast_from_unit(ts, unit)
359- elif is_float_object(ts):
360- ts = _maybe_cast_from_unit(ts, unit)
361- elif isinstance (ts, str ):
362- if (len (ts) > 0 and ts[0 ] == " P" ) or (len (ts) > 1 and ts[:2 ] == " -P" ):
363- ts = parse_iso_format_string(ts)
364- else :
365- ts = parse_timedelta_string(ts)
366- ts = np.timedelta64(ts, " ns" )
367- elif is_tick_object(ts):
368- ts = np.timedelta64(ts.nanos, " ns" )
369346
370- if PyDelta_Check(ts):
347+ elif PyDelta_Check(ts):
371348 ts = np.timedelta64(delta_to_nanoseconds(ts), " ns" )
372349 elif not cnp.is_timedelta64_object(ts):
373350 raise TypeError (f" Invalid type for timedelta scalar: {type(ts)}" )
374351 return ts.astype(" timedelta64[ns]" )
375352
376353
377- cdef _maybe_cast_from_unit(ts , str unit):
354+ cdef _numeric_to_td64ns( object item , str unit):
378355 # caller is responsible for checking
379356 # assert unit not in ["Y", "y", "M"]
357+ # assert is_integer_object(item) or is_float_object(item)
358+ if is_integer_object(item) and item == NPY_NAT:
359+ return np.timedelta64(NPY_NAT, " ns" )
360+
380361 try :
381- ts = cast_from_unit(ts , unit)
362+ item = cast_from_unit(item , unit)
382363 except OutOfBoundsDatetime as err:
383364 raise OutOfBoundsTimedelta(
384- f" Cannot cast {ts } from {unit} to 'ns' without overflow."
365+ f" Cannot cast {item } from {unit} to 'ns' without overflow."
385366 ) from err
386367
387- ts = np.timedelta64(ts , " ns" )
368+ ts = np.timedelta64(item , " ns" )
388369 return ts
389370
390371
@@ -408,10 +389,11 @@ def array_to_timedelta64(
408389 cdef:
409390 Py_ssize_t i , n = values.size
410391 ndarray result = np.empty((< object > values).shape, dtype = " m8[ns]" )
411- object item
392+ object item , td64ns_obj
412393 int64_t ival
413394 cnp.broadcast mi = cnp.PyArray_MultiIterNew2(result, values)
414395 cnp.flatiter it
396+ str parsed_unit = parse_timedelta_unit(unit or " ns" )
415397
416398 if values.descr.type_num != cnp.NPY_OBJECT:
417399 # raise here otherwise we segfault below
@@ -431,70 +413,63 @@ def array_to_timedelta64(
431413 )
432414 cnp.PyArray_ITER_NEXT(it)
433415
434- # Usually, we have all strings. If so, we hit the fast path.
435- # If this path fails, we try conversion a different way, and
436- # this is where all of the error handling will take place.
437- try :
438- for i in range (n):
439- # Analogous to: item = values[i]
440- item = < object > (< PyObject** > cnp.PyArray_MultiIter_DATA(mi, 1 ))[0 ]
416+ for i in range (n):
417+ item = < object > (< PyObject** > cnp.PyArray_MultiIter_DATA(mi, 1 ))[0 ]
441418
442- ival = _item_to_timedelta64_fastpath(item)
419+ try :
420+ if checknull_with_nat_and_na(item):
421+ ival = NPY_NAT
443422
444- # Analogous to: iresult[i] = ival
445- (< int64_t* > cnp.PyArray_MultiIter_DATA(mi, 0 ))[0 ] = ival
423+ elif cnp.is_timedelta64_object(item):
424+ td64ns_obj = ensure_td64ns(item)
425+ ival = cnp.get_timedelta64_value(td64ns_obj)
446426
447- cnp.PyArray_MultiIter_NEXT(mi)
427+ elif isinstance (item, _Timedelta):
428+ if item._creso != NPY_FR_ns:
429+ ival = item.as_unit(" ns" )._value
430+ else :
431+ ival = item._value
432+
433+ elif PyDelta_Check(item):
434+ # i.e. isinstance(item, timedelta)
435+ ival = delta_to_nanoseconds(item)
436+
437+ elif isinstance (item, str ):
438+ if (
439+ (len (item) > 0 and item[0 ] == " P" )
440+ or (len (item) > 1 and item[:2 ] == " -P" )
441+ ):
442+ ival = parse_iso_format_string(item)
443+ else :
444+ ival = parse_timedelta_string(item)
448445
449- except ( TypeError , ValueError ):
450- cnp.PyArray_MultiIter_RESET(mi)
446+ elif is_tick_object(item ):
447+ ival = item.nanos
451448
452- parsed_unit = parse_timedelta_unit(unit or " ns " )
453- for i in range (n):
454- item = < object > ( < PyObject ** > cnp.PyArray_MultiIter_DATA(mi, 1 ))[ 0 ]
449+ elif is_integer_object(item) or is_float_object(item):
450+ td64ns_obj = _numeric_to_td64ns(item, parsed_unit)
451+ ival = cnp.get_timedelta64_value(td64ns_obj)
455452
456- ival = _item_to_timedelta64(item, parsed_unit, errors)
453+ else :
454+ raise TypeError (f" Invalid type for timedelta scalar: {type(item)}" )
455+
456+ except ValueError as err:
457+ if errors == " coerce" :
458+ ival = NPY_NAT
459+ elif " unit abbreviation w/o a number" in str (err):
460+ # re-raise with more pertinent message
461+ msg = f" Could not convert '{item}' to NumPy timedelta"
462+ raise ValueError (msg) from err
463+ else :
464+ raise
457465
458- (< int64_t* > cnp.PyArray_MultiIter_DATA(mi, 0 ))[0 ] = ival
466+ (< int64_t* > cnp.PyArray_MultiIter_DATA(mi, 0 ))[0 ] = ival
459467
460- cnp.PyArray_MultiIter_NEXT(mi)
468+ cnp.PyArray_MultiIter_NEXT(mi)
461469
462470 return result
463471
464472
465- cdef int64_t _item_to_timedelta64_fastpath(object item) except ? - 1 :
466- """
467- See array_to_timedelta64.
468- """
469- if item is NaT:
470- # we allow this check in the fast-path because NaT is a C-object
471- # so this is an inexpensive check
472- return NPY_NAT
473- else :
474- return parse_timedelta_string(item)
475-
476-
477- cdef int64_t _item_to_timedelta64(
478- object item,
479- str parsed_unit,
480- str errors
481- ) except ? - 1 :
482- """
483- See array_to_timedelta64.
484- """
485- try :
486- return cnp.get_timedelta64_value(convert_to_timedelta64(item, parsed_unit))
487- except ValueError as err:
488- if errors == " coerce" :
489- return NPY_NAT
490- elif " unit abbreviation w/o a number" in str (err):
491- # re-raise with more pertinent message
492- msg = f" Could not convert '{item}' to NumPy timedelta"
493- raise ValueError (msg) from err
494- else :
495- raise
496-
497-
498473@ cython.cpow (True )
499474cdef int64_t parse_timedelta_string(str ts) except ? - 1 :
500475 """
@@ -2154,12 +2129,14 @@ class Timedelta(_Timedelta):
21542129 new_value = delta_to_nanoseconds(value, reso = new_reso)
21552130 return cls ._from_value_and_reso(new_value, reso = new_reso)
21562131
2132+ elif checknull_with_nat_and_na(value):
2133+ return NaT
2134+
21572135 elif is_integer_object(value) or is_float_object(value):
21582136 # unit=None is de-facto 'ns'
21592137 unit = parse_timedelta_unit(unit)
2160- value = convert_to_timedelta64(value, unit)
2161- elif checknull_with_nat_and_na(value):
2162- return NaT
2138+ value = _numeric_to_td64ns(value, unit)
2139+
21632140 else :
21642141 raise ValueError (
21652142 " Value must be Timedelta, string, integer, "
0 commit comments