@@ -365,6 +365,21 @@ def _check_date_for_units_since_refdate(
365365 return pd .Timestamp ("NaT" )
366366
367367
368+ def _check_timedelta_range (value , data_unit , time_unit ):
369+ if value > np .iinfo ("int64" ).max or value < np .iinfo ("int64" ).min :
370+ OutOfBoundsTimedelta (f"Value { value } can't be represented as Timedelta." )
371+ delta = value * np .timedelta64 (1 , data_unit )
372+ if not np .isnan (delta ):
373+ # this will raise on dtype overflow for integer dtypes
374+ if value .dtype .kind in "u" and not np .int64 (delta ) == value :
375+ raise OutOfBoundsTimedelta (
376+ "DType overflow in Datetime/Timedelta calculation."
377+ )
378+ # this will raise on overflow if delta cannot be represented with the
379+ # resolutions supported by pandas.
380+ pd .to_timedelta (delta )
381+
382+
368383def _align_reference_date_and_unit (
369384 ref_date : pd .Timestamp , unit : NPDatetimeUnitOptions
370385) -> pd .Timestamp :
@@ -542,19 +557,6 @@ def decode_cf_datetime(
542557 return reshape (dates , num_dates .shape )
543558
544559
545- def to_timedelta_unboxed (value , ** kwargs ):
546- # todo: check, if the procedure here is correct
547- result = pd .to_timedelta (value , ** kwargs ).to_numpy ()
548- unique_timedeltas = np .unique (result [pd .notnull (result )])
549- unit = _netcdf_to_numpy_timeunit (_infer_time_units_from_diff (unique_timedeltas ))
550- if unit not in {"s" , "ms" , "us" , "ns" }:
551- # default to "ns", when not specified
552- unit = "ns"
553- result = result .astype (f"timedelta64[{ unit } ]" )
554- assert np .issubdtype (result .dtype , "timedelta64" )
555- return result
556-
557-
558560def to_datetime_unboxed (value , ** kwargs ):
559561 result = pd .to_datetime (value , ** kwargs ).to_numpy ()
560562 assert np .issubdtype (result .dtype , "datetime64" )
@@ -604,22 +606,36 @@ def _numbers_to_timedelta(
604606 return flat_num .astype (f"timedelta64[{ time_unit } ]" )
605607
606608
607- def decode_cf_timedelta (num_timedeltas , units : str ) -> np .ndarray :
608- # todo: check, if this works as intended
609+ def decode_cf_timedelta (
610+ num_timedeltas , units : str , time_unit : str = "ns"
611+ ) -> np .ndarray :
609612 """Given an array of numeric timedeltas in netCDF format, convert it into a
610613 numpy timedelta64 ["s", "ms", "us", "ns"] array.
611614 """
612615 num_timedeltas = np .asarray (num_timedeltas )
613616 unit = _netcdf_to_numpy_timeunit (units )
614617
618+ _check_timedelta_range (num_timedeltas .min (), unit , time_unit )
619+ _check_timedelta_range (num_timedeltas .max (), unit , time_unit )
620+
615621 timedeltas = _numbers_to_timedelta (num_timedeltas , unit , "s" , "timedelta" )
622+ timedeltas = pd .to_timedelta (ravel (timedeltas ))
623+
624+ if np .isnat (timedeltas ).all ():
625+ empirical_unit = time_unit
626+ else :
627+ empirical_unit = timedeltas .unit
628+
629+ if np .timedelta64 (1 , time_unit ) > np .timedelta64 (1 , empirical_unit ):
630+ time_unit = empirical_unit
631+
632+ if time_unit not in {"s" , "ms" , "us" , "ns" }:
633+ raise ValueError (
634+ f"time_unit must be one of 's', 'ms', 'us', or 'ns'. Got: { time_unit } "
635+ )
616636
617- as_unit = unit
618- if unit not in {"s" , "ms" , "us" , "ns" }:
619- # default to "ns", when not specified
620- as_unit = "ns"
621- result = pd .to_timedelta (ravel (timedeltas )).as_unit (as_unit ).to_numpy ()
622- return reshape (result , timedeltas .shape )
637+ result = timedeltas .as_unit (time_unit ).to_numpy ()
638+ return reshape (result , num_timedeltas .shape )
623639
624640
625641def _unit_timedelta_cftime (units : str ) -> timedelta :
@@ -700,7 +716,7 @@ def infer_timedelta_units(deltas) -> str:
700716 {'days', 'hours', 'minutes' 'seconds'} (the first one that can evenly
701717 divide all unique time deltas in `deltas`)
702718 """
703- deltas = to_timedelta_unboxed ( ravel (np . asarray ( deltas )) )
719+ deltas = ravel (deltas )
704720 unique_timedeltas = np .unique (deltas [pd .notnull (deltas )])
705721 return _infer_time_units_from_diff (unique_timedeltas )
706722
0 commit comments