1212
1313import numpy as np
1414
15- from pandas ._libs import lib , iNaT , NaT
15+ from pandas ._libs import lib , iNaT , NaT , Timedelta
1616from pandas ._libs .tslibs .period import Period
1717from pandas ._libs .tslibs .timedeltas import delta_to_nanoseconds
1818from pandas ._libs .tslibs .timestamps import round_ns
3434 is_string_dtype ,
3535 is_datetime64_dtype ,
3636 is_datetime64tz_dtype ,
37+ is_datetime64_any_dtype ,
3738 is_period_dtype ,
3839 is_timedelta64_dtype )
3940from pandas .core .dtypes .generic import (
@@ -814,6 +815,46 @@ def _addsub_offset_array(self, other, op):
814815 kwargs ['freq' ] = 'infer'
815816 return self ._constructor (res_values , ** kwargs )
816817
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+
817858 @classmethod
818859 def _add_datetimelike_methods (cls ):
819860 """
@@ -822,8 +863,6 @@ def _add_datetimelike_methods(cls):
822863 """
823864
824865 def __add__ (self , other ):
825- from pandas import DateOffset
826-
827866 other = lib .item_from_zerodim (other )
828867 if isinstance (other , (ABCSeries , ABCDataFrame )):
829868 return NotImplemented
@@ -853,9 +892,8 @@ def __add__(self, other):
853892 elif is_datetime64_dtype (other ) or is_datetime64tz_dtype (other ):
854893 # DatetimeIndex, ndarray[datetime64]
855894 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 )
859897 elif is_float_dtype (other ):
860898 # Explicitly catch invalid dtypes
861899 raise TypeError ("cannot add {dtype}-dtype to {cls}"
@@ -915,14 +953,12 @@ def __sub__(self, other):
915953 elif is_datetime64_dtype (other ) or is_datetime64tz_dtype (other ):
916954 # DatetimeIndex, ndarray[datetime64]
917955 result = self ._sub_datelike (other )
956+ elif is_integer_dtype (other ):
957+ result = self ._addsub_int_array (other , operator .sub )
918958 elif isinstance (other , Index ):
919959 raise TypeError ("cannot subtract {cls} and {typ}"
920960 .format (cls = type (self ).__name__ ,
921961 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-
926962 elif is_float_dtype (other ):
927963 # Explicitly catch invalid dtypes
928964 raise TypeError ("cannot subtract {dtype}-dtype from {cls}"
@@ -948,6 +984,13 @@ def __rsub__(self, other):
948984 # we need to wrap in DatetimeIndex and flip the operation
949985 from pandas import DatetimeIndex
950986 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__ ))
951994 return - (self - other )
952995 cls .__rsub__ = __rsub__
953996
0 commit comments