12
12
13
13
import numpy as np
14
14
15
- from pandas ._libs import lib , iNaT , NaT
15
+ from pandas ._libs import lib , iNaT , NaT , Timedelta
16
16
from pandas ._libs .tslibs .period import Period
17
17
from pandas ._libs .tslibs .timedeltas import delta_to_nanoseconds
18
18
from pandas ._libs .tslibs .timestamps import round_ns
34
34
is_string_dtype ,
35
35
is_datetime64_dtype ,
36
36
is_datetime64tz_dtype ,
37
+ is_datetime64_any_dtype ,
37
38
is_period_dtype ,
38
39
is_timedelta64_dtype )
39
40
from pandas .core .dtypes .generic import (
@@ -814,6 +815,46 @@ def _addsub_offset_array(self, other, op):
814
815
kwargs ['freq' ] = 'infer'
815
816
return self ._constructor (res_values , ** kwargs )
816
817
818
+ def _addsub_int_array (self , other , op ):
819
+ """
820
+ Add or subtract array-like of integers equivalent to applying
821
+ `shift` pointwise.
822
+
823
+ Parameters
824
+ ----------
825
+ other : Index, np.ndarray
826
+ integer-dtype
827
+ op : {operator.add, operator.sub}
828
+
829
+ Returns
830
+ -------
831
+ result : same class as self
832
+ """
833
+ assert op in [operator .add , operator .sub ]
834
+ if is_period_dtype (self ):
835
+ # easy case for PeriodIndex
836
+ if op is operator .sub :
837
+ other = - other
838
+ res_values = checked_add_with_arr (self .asi8 , other ,
839
+ arr_mask = self ._isnan )
840
+ res_values = res_values .view ('i8' )
841
+ res_values [self ._isnan ] = iNaT
842
+ return self ._from_ordinals (res_values , freq = self .freq )
843
+
844
+ elif self .freq is None :
845
+ # GH#19123
846
+ raise NullFrequencyError ("Cannot shift with no freq" )
847
+
848
+ elif isinstance (self .freq , Tick ):
849
+ # easy case where we can convert to timedelta64 operation
850
+ td = Timedelta (self .freq )
851
+ return op (self , td * other )
852
+
853
+ # We should only get here with DatetimeIndex; dispatch
854
+ # to _addsub_offset_array
855
+ assert not is_timedelta64_dtype (self )
856
+ return op (self , np .array (other ) * self .freq )
857
+
817
858
@classmethod
818
859
def _add_datetimelike_methods (cls ):
819
860
"""
@@ -822,8 +863,6 @@ def _add_datetimelike_methods(cls):
822
863
"""
823
864
824
865
def __add__ (self , other ):
825
- from pandas import DateOffset
826
-
827
866
other = lib .item_from_zerodim (other )
828
867
if isinstance (other , (ABCSeries , ABCDataFrame )):
829
868
return NotImplemented
@@ -853,9 +892,8 @@ def __add__(self, other):
853
892
elif is_datetime64_dtype (other ) or is_datetime64tz_dtype (other ):
854
893
# DatetimeIndex, ndarray[datetime64]
855
894
return self ._add_datelike (other )
856
- elif is_integer_dtype (other ) and self .freq is None :
857
- # GH#19123
858
- raise NullFrequencyError ("Cannot shift with no freq" )
895
+ elif is_integer_dtype (other ):
896
+ result = self ._addsub_int_array (other , operator .add )
859
897
elif is_float_dtype (other ):
860
898
# Explicitly catch invalid dtypes
861
899
raise TypeError ("cannot add {dtype}-dtype to {cls}"
@@ -915,14 +953,12 @@ def __sub__(self, other):
915
953
elif is_datetime64_dtype (other ) or is_datetime64tz_dtype (other ):
916
954
# DatetimeIndex, ndarray[datetime64]
917
955
result = self ._sub_datelike (other )
956
+ elif is_integer_dtype (other ):
957
+ result = self ._addsub_int_array (other , operator .sub )
918
958
elif isinstance (other , Index ):
919
959
raise TypeError ("cannot subtract {cls} and {typ}"
920
960
.format (cls = type (self ).__name__ ,
921
961
typ = type (other ).__name__ ))
922
- elif is_integer_dtype (other ) and self .freq is None :
923
- # GH#19123
924
- raise NullFrequencyError ("Cannot shift with no freq" )
925
-
926
962
elif is_float_dtype (other ):
927
963
# Explicitly catch invalid dtypes
928
964
raise TypeError ("cannot subtract {dtype}-dtype from {cls}"
@@ -948,6 +984,13 @@ def __rsub__(self, other):
948
984
# we need to wrap in DatetimeIndex and flip the operation
949
985
from pandas import DatetimeIndex
950
986
return DatetimeIndex (other ) - self
987
+ elif (is_datetime64_any_dtype (self ) and hasattr (other , 'dtype' ) and
988
+ not is_datetime64_any_dtype (other )):
989
+ # GH#19959 datetime - datetime is well-defined as timedelta,
990
+ # but any other type - datetime is not well-defined.
991
+ raise TypeError ("cannot subtract {cls} from {typ}"
992
+ .format (cls = type (self ).__name__ ,
993
+ typ = type (other ).__name__ ))
951
994
return - (self - other )
952
995
cls .__rsub__ = __rsub__
953
996
0 commit comments