14
14
15
15
import numpy as np
16
16
17
- from pandas ._libs import algos as libalgos
17
+ from pandas ._libs import (
18
+ algos as libalgos ,
19
+ lib ,
20
+ )
18
21
from pandas ._libs .arrays import NDArrayBacked
19
22
from pandas ._libs .tslibs import (
20
23
BaseOffset ,
21
24
NaT ,
22
25
NaTType ,
23
26
Timedelta ,
24
27
astype_overflowsafe ,
25
- delta_to_nanoseconds ,
26
28
dt64arr_to_periodarr as c_dt64arr_to_periodarr ,
27
29
get_unit_from_dtype ,
28
30
iNaT ,
55
57
)
56
58
57
59
from pandas .core .dtypes .common import (
58
- TD64NS_DTYPE ,
59
60
ensure_object ,
60
61
is_datetime64_any_dtype ,
61
62
is_datetime64_dtype ,
72
73
ABCSeries ,
73
74
ABCTimedeltaArray ,
74
75
)
75
- from pandas .core .dtypes .missing import notna
76
+ from pandas .core .dtypes .missing import isna
76
77
77
78
import pandas .core .algorithms as algos
78
79
from pandas .core .arrays import datetimelike as dtl
@@ -764,30 +765,24 @@ def _add_timedeltalike_scalar(self, other):
764
765
# We cannot add timedelta-like to non-tick PeriodArray
765
766
raise raise_on_incompatible (self , other )
766
767
767
- if notna (other ):
768
- # Convert to an integer increment of our own freq, disallowing
769
- # e.g. 30seconds if our freq is minutes.
770
- try :
771
- inc = delta_to_nanoseconds (other , reso = self .freq ._reso , round_ok = False )
772
- except ValueError as err :
773
- # "Cannot losslessly convert units"
774
- raise raise_on_incompatible (self , other ) from err
775
-
776
- return self ._addsub_int_array_or_scalar (inc , operator .add )
768
+ if isna (other ):
769
+ # i.e. np.timedelta64("NaT")
770
+ return super ()._add_timedeltalike_scalar (other )
777
771
778
- return super ()._add_timedeltalike_scalar (other )
772
+ td = np .asarray (Timedelta (other ).asm8 )
773
+ return self ._add_timedelta_arraylike (td )
779
774
780
775
def _add_timedelta_arraylike (
781
776
self , other : TimedeltaArray | npt .NDArray [np .timedelta64 ]
782
- ):
777
+ ) -> PeriodArray :
783
778
"""
784
779
Parameters
785
780
----------
786
781
other : TimedeltaArray or ndarray[timedelta64]
787
782
788
783
Returns
789
784
-------
790
- result : ndarray[int64]
785
+ PeriodArray
791
786
"""
792
787
freq = self .freq
793
788
if not isinstance (freq , Tick ):
@@ -803,8 +798,12 @@ def _add_timedelta_arraylike(
803
798
np .asarray (other ), dtype = dtype , copy = False , round_ok = False
804
799
)
805
800
except ValueError as err :
806
- # TODO: not actually a great exception message in this case
807
- raise raise_on_incompatible (self , other ) from err
801
+ # e.g. if we have minutes freq and try to add 30s
802
+ # "Cannot losslessly convert units"
803
+ raise IncompatibleFrequency (
804
+ "Cannot add/subtract timedelta-like from PeriodArray that is "
805
+ "not an integer multiple of the PeriodArray's freq."
806
+ ) from err
808
807
809
808
b_mask = np .isnat (delta )
810
809
@@ -835,31 +834,21 @@ def _check_timedeltalike_freq_compat(self, other):
835
834
IncompatibleFrequency
836
835
"""
837
836
assert isinstance (self .freq , Tick ) # checked by calling function
838
- base_nanos = self .freq .base .nanos
837
+
838
+ dtype = np .dtype (f"m8[{ self .freq ._td64_unit } ]" )
839
839
840
840
if isinstance (other , (timedelta , np .timedelta64 , Tick )):
841
- nanos = delta_to_nanoseconds (other )
842
-
843
- elif isinstance (other , np .ndarray ):
844
- # numpy timedelta64 array; all entries must be compatible
845
- assert other .dtype .kind == "m"
846
- other = astype_overflowsafe (other , TD64NS_DTYPE , copy = False )
847
- # error: Incompatible types in assignment (expression has type
848
- # "ndarray[Any, dtype[Any]]", variable has type "int")
849
- nanos = other .view ("i8" ) # type: ignore[assignment]
841
+ td = np .asarray (Timedelta (other ).asm8 )
850
842
else :
851
- # TimedeltaArray/Index
852
- nanos = other .asi8
853
-
854
- if np .all (nanos % base_nanos == 0 ):
855
- # nanos being added is an integer multiple of the
856
- # base-frequency to self.freq
857
- delta = nanos // base_nanos
858
- # delta is the integer (or integer-array) number of periods
859
- # by which will be added to self.
860
- return delta
861
-
862
- raise raise_on_incompatible (self , other )
843
+ td = np .asarray (other )
844
+
845
+ try :
846
+ delta = astype_overflowsafe (td , dtype = dtype , copy = False , round_ok = False )
847
+ except ValueError as err :
848
+ raise raise_on_incompatible (self , other ) from err
849
+
850
+ delta = delta .view ("i8" )
851
+ return lib .item_from_zerodim (delta )
863
852
864
853
865
854
def raise_on_incompatible (left , right ):
0 commit comments