@@ -85,13 +85,14 @@ def na_op(x, y):
8585 def wrapper (self , other , name = name ):
8686 from pandas .core .frame import DataFrame
8787 dtype = None
88+ fill_value = tslib .iNaT
8889 wrap_results = lambda x : x
8990
9091 lvalues , rvalues = self , other
9192
9293 is_timedelta_lhs = com .is_timedelta64_dtype (self )
9394 is_datetime_lhs = com .is_datetime64_dtype (self )
94- is_integer_lhs = lvalues .dtype == np . int64
95+ is_integer_lhs = lvalues .dtype . kind in [ 'i' , 'u' ]
9596
9697 if is_datetime_lhs or is_timedelta_lhs :
9798
@@ -116,14 +117,19 @@ def convert_to_array(values):
116117 # py3 compat where dtype is 'm' but is an integer
117118 if values .dtype .kind == 'm' :
118119 values = values .astype ('timedelta64[ns]' )
119- elif name not in ['__div__' ,'__mul__' ]:
120- raise TypeError ("incompatible type for a datetime/timedelta operation" )
120+ elif name not in ['__truediv__' , ' __div__' ,'__mul__' ]:
121+ raise TypeError ("incompatible type for a datetime/timedelta operation [{0}]" . format ( name ) )
121122 elif isinstance (values [0 ],DateOffset ):
122123 # handle DateOffsets
123- values = pa .array ([ v .delta for v in values ])
124- values = com ._possibly_cast_to_timedelta (values , coerce = coerce )
124+ os = pa .array ([ getattr (v ,'delta' ,None ) for v in values ])
125+ mask = isnull (os )
126+ if mask .any ():
127+ raise TypeError ("cannot use a non-absolute DateOffset in "
128+ "datetime/timedelta operations [{0}]" .format (',' .join ([ com .pprint_thing (v ) for v in values [mask ] ])))
129+ values = com ._possibly_cast_to_timedelta (os , coerce = coerce )
125130 else :
126- values = pa .array (values )
131+ raise TypeError ("incompatible type [{0}] for a datetime/timedelta operation" .format (pa .array (values ).dtype ))
132+
127133 return values
128134
129135 # convert lhs and rhs
@@ -132,32 +138,51 @@ def convert_to_array(values):
132138
133139 is_datetime_rhs = com .is_datetime64_dtype (rvalues )
134140 is_timedelta_rhs = com .is_timedelta64_dtype (rvalues ) or (not is_datetime_rhs and _np_version_under1p7 )
135- is_integer_rhs = rvalues .dtype == np . int64
141+ is_integer_rhs = rvalues .dtype . kind in [ 'i' , 'u' ]
136142 mask = None
137143
138144 # timedelta and integer mul/div
139145 if (is_timedelta_lhs and is_integer_rhs ) or (is_integer_lhs and is_timedelta_rhs ):
140146
141- if name not in ['__div__' ,'__mul__' ]:
147+ if name not in ['__truediv__' , ' __div__' ,'__mul__' ]:
142148 raise TypeError ("can only operate on a timedelta and an integer for "
143149 "division, but the operator [%s] was passed" % name )
144150 dtype = 'timedelta64[ns]'
145151 mask = isnull (lvalues ) | isnull (rvalues )
146152 lvalues = lvalues .astype (np .int64 )
147153 rvalues = rvalues .astype (np .int64 )
148154
149- # 2 datetimes or 2 timedeltas
150- elif (is_timedelta_lhs and is_timedelta_rhs ) or (is_datetime_lhs and
151- is_datetime_rhs ):
152- if is_datetime_lhs and name != '__sub__' :
155+ # 2 datetimes
156+ elif is_datetime_lhs and is_datetime_rhs :
157+ if name != '__sub__' :
153158 raise TypeError ("can only operate on a datetimes for subtraction, "
154159 "but the operator [%s] was passed" % name )
155- elif is_timedelta_lhs and name not in ['__add__' ,'__sub__' ]:
156- raise TypeError ("can only operate on a timedeltas for "
157- "addition and subtraction, but the operator [%s] was passed" % name )
158160
159161 dtype = 'timedelta64[ns]'
160162 mask = isnull (lvalues ) | isnull (rvalues )
163+ lvalues = lvalues .view ('i8' )
164+ rvalues = rvalues .view ('i8' )
165+
166+ # 2 timedeltas
167+ elif is_timedelta_lhs and is_timedelta_rhs :
168+ mask = isnull (lvalues ) | isnull (rvalues )
169+
170+ # time delta division -> unit less
171+ if name in ['__div__' ,'__truediv__' ]:
172+ dtype = 'float64'
173+ fill_value = np .nan
174+ lvalues = lvalues .astype (np .int64 ).astype (np .float64 )
175+ rvalues = rvalues .astype (np .int64 ).astype (np .float64 )
176+
177+ # another timedelta
178+ elif name in ['__add__' ,'__sub__' ]:
179+ dtype = 'timedelta64[ns]'
180+ lvalues = lvalues .astype (np .int64 )
181+ rvalues = rvalues .astype (np .int64 )
182+
183+ else :
184+ raise TypeError ("can only operate on a timedeltas for "
185+ "addition, subtraction, and division, but the operator [%s] was passed" % name )
161186
162187 # datetime and timedelta
163188 elif is_timedelta_rhs and is_datetime_lhs :
@@ -166,13 +191,17 @@ def convert_to_array(values):
166191 raise TypeError ("can only operate on a datetime with a rhs of a timedelta for "
167192 "addition and subtraction, but the operator [%s] was passed" % name )
168193 dtype = 'M8[ns]'
194+ lvalues = lvalues .view ('i8' )
195+ rvalues = rvalues .view ('i8' )
169196
170197 elif is_timedelta_lhs and is_datetime_rhs :
171198
172199 if name not in ['__add__' ]:
173200 raise TypeError ("can only operate on a timedelta and a datetime for "
174201 "addition, but the operator [%s] was passed" % name )
175202 dtype = 'M8[ns]'
203+ lvalues = lvalues .view ('i8' )
204+ rvalues = rvalues .view ('i8' )
176205
177206 else :
178207 raise TypeError ('cannot operate on a series with out a rhs '
@@ -183,14 +212,11 @@ def convert_to_array(values):
183212 if mask is not None :
184213 if mask .any ():
185214 def f (x ):
186- x = pa .array (x ,dtype = 'timedelta64[ns]' )
187- np .putmask (x ,mask ,tslib . iNaT )
215+ x = pa .array (x ,dtype = dtype )
216+ np .putmask (x ,mask ,fill_value )
188217 return x
189218 wrap_results = f
190219
191- lvalues = lvalues .view ('i8' )
192- rvalues = rvalues .view ('i8' )
193-
194220 if isinstance (rvalues , Series ):
195221
196222 if hasattr (lvalues ,'values' ):
0 commit comments