test_arithmetic.py 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181
  1. """
  2. Tests for scalar Timedelta arithmetic ops
  3. """
  4. from datetime import (
  5. datetime,
  6. timedelta,
  7. )
  8. import operator
  9. import numpy as np
  10. import pytest
  11. from pandas.errors import OutOfBoundsTimedelta
  12. import pandas as pd
  13. from pandas import (
  14. NaT,
  15. Timedelta,
  16. Timestamp,
  17. offsets,
  18. )
  19. import pandas._testing as tm
  20. from pandas.core import ops
  21. class TestTimedeltaAdditionSubtraction:
  22. """
  23. Tests for Timedelta methods:
  24. __add__, __radd__,
  25. __sub__, __rsub__
  26. """
  27. @pytest.mark.parametrize(
  28. "ten_seconds",
  29. [
  30. Timedelta(10, unit="s"),
  31. timedelta(seconds=10),
  32. np.timedelta64(10, "s"),
  33. np.timedelta64(10000000000, "ns"),
  34. offsets.Second(10),
  35. ],
  36. )
  37. def test_td_add_sub_ten_seconds(self, ten_seconds):
  38. # GH#6808
  39. base = Timestamp("20130101 09:01:12.123456")
  40. expected_add = Timestamp("20130101 09:01:22.123456")
  41. expected_sub = Timestamp("20130101 09:01:02.123456")
  42. result = base + ten_seconds
  43. assert result == expected_add
  44. result = base - ten_seconds
  45. assert result == expected_sub
  46. @pytest.mark.parametrize(
  47. "one_day_ten_secs",
  48. [
  49. Timedelta("1 day, 00:00:10"),
  50. Timedelta("1 days, 00:00:10"),
  51. timedelta(days=1, seconds=10),
  52. np.timedelta64(1, "D") + np.timedelta64(10, "s"),
  53. offsets.Day() + offsets.Second(10),
  54. ],
  55. )
  56. def test_td_add_sub_one_day_ten_seconds(self, one_day_ten_secs):
  57. # GH#6808
  58. base = Timestamp("20130102 09:01:12.123456")
  59. expected_add = Timestamp("20130103 09:01:22.123456")
  60. expected_sub = Timestamp("20130101 09:01:02.123456")
  61. result = base + one_day_ten_secs
  62. assert result == expected_add
  63. result = base - one_day_ten_secs
  64. assert result == expected_sub
  65. @pytest.mark.parametrize("op", [operator.add, ops.radd])
  66. def test_td_add_datetimelike_scalar(self, op):
  67. # GH#19738
  68. td = Timedelta(10, unit="d")
  69. result = op(td, datetime(2016, 1, 1))
  70. if op is operator.add:
  71. # datetime + Timedelta does _not_ call Timedelta.__radd__,
  72. # so we get a datetime back instead of a Timestamp
  73. assert isinstance(result, Timestamp)
  74. assert result == Timestamp(2016, 1, 11)
  75. result = op(td, Timestamp("2018-01-12 18:09"))
  76. assert isinstance(result, Timestamp)
  77. assert result == Timestamp("2018-01-22 18:09")
  78. result = op(td, np.datetime64("2018-01-12"))
  79. assert isinstance(result, Timestamp)
  80. assert result == Timestamp("2018-01-22")
  81. result = op(td, NaT)
  82. assert result is NaT
  83. def test_td_add_timestamp_overflow(self):
  84. ts = Timestamp("1700-01-01").as_unit("ns")
  85. msg = "Cannot cast 259987 from D to 'ns' without overflow."
  86. with pytest.raises(OutOfBoundsTimedelta, match=msg):
  87. ts + Timedelta(13 * 19999, unit="D")
  88. msg = "Cannot cast 259987 days 00:00:00 to unit='ns' without overflow"
  89. with pytest.raises(OutOfBoundsTimedelta, match=msg):
  90. ts + timedelta(days=13 * 19999)
  91. @pytest.mark.parametrize("op", [operator.add, ops.radd])
  92. def test_td_add_td(self, op):
  93. td = Timedelta(10, unit="d")
  94. result = op(td, Timedelta(days=10))
  95. assert isinstance(result, Timedelta)
  96. assert result == Timedelta(days=20)
  97. @pytest.mark.parametrize("op", [operator.add, ops.radd])
  98. def test_td_add_pytimedelta(self, op):
  99. td = Timedelta(10, unit="d")
  100. result = op(td, timedelta(days=9))
  101. assert isinstance(result, Timedelta)
  102. assert result == Timedelta(days=19)
  103. @pytest.mark.parametrize("op", [operator.add, ops.radd])
  104. def test_td_add_timedelta64(self, op):
  105. td = Timedelta(10, unit="d")
  106. result = op(td, np.timedelta64(-4, "D"))
  107. assert isinstance(result, Timedelta)
  108. assert result == Timedelta(days=6)
  109. @pytest.mark.parametrize("op", [operator.add, ops.radd])
  110. def test_td_add_offset(self, op):
  111. td = Timedelta(10, unit="d")
  112. result = op(td, offsets.Hour(6))
  113. assert isinstance(result, Timedelta)
  114. assert result == Timedelta(days=10, hours=6)
  115. def test_td_sub_td(self):
  116. td = Timedelta(10, unit="d")
  117. expected = Timedelta(0, unit="ns")
  118. result = td - td
  119. assert isinstance(result, Timedelta)
  120. assert result == expected
  121. def test_td_sub_pytimedelta(self):
  122. td = Timedelta(10, unit="d")
  123. expected = Timedelta(0, unit="ns")
  124. result = td - td.to_pytimedelta()
  125. assert isinstance(result, Timedelta)
  126. assert result == expected
  127. result = td.to_pytimedelta() - td
  128. assert isinstance(result, Timedelta)
  129. assert result == expected
  130. def test_td_sub_timedelta64(self):
  131. td = Timedelta(10, unit="d")
  132. expected = Timedelta(0, unit="ns")
  133. result = td - td.to_timedelta64()
  134. assert isinstance(result, Timedelta)
  135. assert result == expected
  136. result = td.to_timedelta64() - td
  137. assert isinstance(result, Timedelta)
  138. assert result == expected
  139. def test_td_sub_nat(self):
  140. # In this context pd.NaT is treated as timedelta-like
  141. td = Timedelta(10, unit="d")
  142. result = td - NaT
  143. assert result is NaT
  144. def test_td_sub_td64_nat(self):
  145. td = Timedelta(10, unit="d")
  146. td_nat = np.timedelta64("NaT")
  147. result = td - td_nat
  148. assert result is NaT
  149. result = td_nat - td
  150. assert result is NaT
  151. def test_td_sub_offset(self):
  152. td = Timedelta(10, unit="d")
  153. result = td - offsets.Hour(1)
  154. assert isinstance(result, Timedelta)
  155. assert result == Timedelta(239, unit="h")
  156. def test_td_add_sub_numeric_raises(self):
  157. td = Timedelta(10, unit="d")
  158. msg = "unsupported operand type"
  159. for other in [2, 2.0, np.int64(2), np.float64(2)]:
  160. with pytest.raises(TypeError, match=msg):
  161. td + other
  162. with pytest.raises(TypeError, match=msg):
  163. other + td
  164. with pytest.raises(TypeError, match=msg):
  165. td - other
  166. with pytest.raises(TypeError, match=msg):
  167. other - td
  168. def test_td_add_sub_int_ndarray(self):
  169. td = Timedelta("1 day")
  170. other = np.array([1])
  171. msg = r"unsupported operand type\(s\) for \+: 'Timedelta' and 'int'"
  172. with pytest.raises(TypeError, match=msg):
  173. td + np.array([1])
  174. msg = "|".join(
  175. [
  176. (
  177. r"unsupported operand type\(s\) for \+: 'numpy.ndarray' "
  178. "and 'Timedelta'"
  179. ),
  180. # This message goes on to say "Please do not rely on this error;
  181. # it may not be given on all Python implementations"
  182. "Concatenation operation is not implemented for NumPy arrays",
  183. ]
  184. )
  185. with pytest.raises(TypeError, match=msg):
  186. other + td
  187. msg = r"unsupported operand type\(s\) for -: 'Timedelta' and 'int'"
  188. with pytest.raises(TypeError, match=msg):
  189. td - other
  190. msg = r"unsupported operand type\(s\) for -: 'numpy.ndarray' and 'Timedelta'"
  191. with pytest.raises(TypeError, match=msg):
  192. other - td
  193. def test_td_rsub_nat(self):
  194. td = Timedelta(10, unit="d")
  195. result = NaT - td
  196. assert result is NaT
  197. result = np.datetime64("NaT") - td
  198. assert result is NaT
  199. def test_td_rsub_offset(self):
  200. result = offsets.Hour(1) - Timedelta(10, unit="d")
  201. assert isinstance(result, Timedelta)
  202. assert result == Timedelta(-239, unit="h")
  203. def test_td_sub_timedeltalike_object_dtype_array(self):
  204. # GH#21980
  205. arr = np.array([Timestamp("20130101 9:01"), Timestamp("20121230 9:02")])
  206. exp = np.array([Timestamp("20121231 9:01"), Timestamp("20121229 9:02")])
  207. res = arr - Timedelta("1D")
  208. tm.assert_numpy_array_equal(res, exp)
  209. def test_td_sub_mixed_most_timedeltalike_object_dtype_array(self):
  210. # GH#21980
  211. now = Timestamp("2021-11-09 09:54:00")
  212. arr = np.array([now, Timedelta("1D"), np.timedelta64(2, "h")])
  213. exp = np.array(
  214. [
  215. now - Timedelta("1D"),
  216. Timedelta("0D"),
  217. np.timedelta64(2, "h") - Timedelta("1D"),
  218. ]
  219. )
  220. res = arr - Timedelta("1D")
  221. tm.assert_numpy_array_equal(res, exp)
  222. def test_td_rsub_mixed_most_timedeltalike_object_dtype_array(self):
  223. # GH#21980
  224. now = Timestamp("2021-11-09 09:54:00")
  225. arr = np.array([now, Timedelta("1D"), np.timedelta64(2, "h")])
  226. msg = r"unsupported operand type\(s\) for \-: 'Timedelta' and 'Timestamp'"
  227. with pytest.raises(TypeError, match=msg):
  228. Timedelta("1D") - arr
  229. @pytest.mark.parametrize("op", [operator.add, ops.radd])
  230. def test_td_add_timedeltalike_object_dtype_array(self, op):
  231. # GH#21980
  232. arr = np.array([Timestamp("20130101 9:01"), Timestamp("20121230 9:02")])
  233. exp = np.array([Timestamp("20130102 9:01"), Timestamp("20121231 9:02")])
  234. res = op(arr, Timedelta("1D"))
  235. tm.assert_numpy_array_equal(res, exp)
  236. @pytest.mark.parametrize("op", [operator.add, ops.radd])
  237. def test_td_add_mixed_timedeltalike_object_dtype_array(self, op):
  238. # GH#21980
  239. now = Timestamp("2021-11-09 09:54:00")
  240. arr = np.array([now, Timedelta("1D")])
  241. exp = np.array([now + Timedelta("1D"), Timedelta("2D")])
  242. res = op(arr, Timedelta("1D"))
  243. tm.assert_numpy_array_equal(res, exp)
  244. def test_td_add_sub_td64_ndarray(self):
  245. td = Timedelta("1 day")
  246. other = np.array([td.to_timedelta64()])
  247. expected = np.array([Timedelta("2 Days").to_timedelta64()])
  248. result = td + other
  249. tm.assert_numpy_array_equal(result, expected)
  250. result = other + td
  251. tm.assert_numpy_array_equal(result, expected)
  252. result = td - other
  253. tm.assert_numpy_array_equal(result, expected * 0)
  254. result = other - td
  255. tm.assert_numpy_array_equal(result, expected * 0)
  256. def test_td_add_sub_dt64_ndarray(self):
  257. td = Timedelta("1 day")
  258. other = pd.to_datetime(["2000-01-01"]).values
  259. expected = pd.to_datetime(["2000-01-02"]).values
  260. tm.assert_numpy_array_equal(td + other, expected)
  261. tm.assert_numpy_array_equal(other + td, expected)
  262. expected = pd.to_datetime(["1999-12-31"]).values
  263. tm.assert_numpy_array_equal(-td + other, expected)
  264. tm.assert_numpy_array_equal(other - td, expected)
  265. def test_td_add_sub_ndarray_0d(self):
  266. td = Timedelta("1 day")
  267. other = np.array(td.asm8)
  268. result = td + other
  269. assert isinstance(result, Timedelta)
  270. assert result == 2 * td
  271. result = other + td
  272. assert isinstance(result, Timedelta)
  273. assert result == 2 * td
  274. result = other - td
  275. assert isinstance(result, Timedelta)
  276. assert result == 0 * td
  277. result = td - other
  278. assert isinstance(result, Timedelta)
  279. assert result == 0 * td
  280. class TestTimedeltaMultiplicationDivision:
  281. """
  282. Tests for Timedelta methods:
  283. __mul__, __rmul__,
  284. __div__, __rdiv__,
  285. __truediv__, __rtruediv__,
  286. __floordiv__, __rfloordiv__,
  287. __mod__, __rmod__,
  288. __divmod__, __rdivmod__
  289. """
  290. # ---------------------------------------------------------------
  291. # Timedelta.__mul__, __rmul__
  292. @pytest.mark.parametrize(
  293. "td_nat", [NaT, np.timedelta64("NaT", "ns"), np.timedelta64("NaT")]
  294. )
  295. @pytest.mark.parametrize("op", [operator.mul, ops.rmul])
  296. def test_td_mul_nat(self, op, td_nat):
  297. # GH#19819
  298. td = Timedelta(10, unit="d")
  299. typs = "|".join(["numpy.timedelta64", "NaTType", "Timedelta"])
  300. msg = "|".join(
  301. [
  302. rf"unsupported operand type\(s\) for \*: '{typs}' and '{typs}'",
  303. r"ufunc '?multiply'? cannot use operands with types",
  304. ]
  305. )
  306. with pytest.raises(TypeError, match=msg):
  307. op(td, td_nat)
  308. @pytest.mark.parametrize("nan", [np.nan, np.float64("NaN"), float("nan")])
  309. @pytest.mark.parametrize("op", [operator.mul, ops.rmul])
  310. def test_td_mul_nan(self, op, nan):
  311. # np.float64('NaN') has a 'dtype' attr, avoid treating as array
  312. td = Timedelta(10, unit="d")
  313. result = op(td, nan)
  314. assert result is NaT
  315. @pytest.mark.parametrize("op", [operator.mul, ops.rmul])
  316. def test_td_mul_scalar(self, op):
  317. # GH#19738
  318. td = Timedelta(minutes=3)
  319. result = op(td, 2)
  320. assert result == Timedelta(minutes=6)
  321. result = op(td, 1.5)
  322. assert result == Timedelta(minutes=4, seconds=30)
  323. assert op(td, np.nan) is NaT
  324. assert op(-1, td)._value == -1 * td._value
  325. assert op(-1.0, td)._value == -1.0 * td._value
  326. msg = "unsupported operand type"
  327. with pytest.raises(TypeError, match=msg):
  328. # timedelta * datetime is gibberish
  329. op(td, Timestamp(2016, 1, 2))
  330. with pytest.raises(TypeError, match=msg):
  331. # invalid multiply with another timedelta
  332. op(td, td)
  333. def test_td_mul_numeric_ndarray(self):
  334. td = Timedelta("1 day")
  335. other = np.array([2])
  336. expected = np.array([Timedelta("2 Days").to_timedelta64()])
  337. result = td * other
  338. tm.assert_numpy_array_equal(result, expected)
  339. result = other * td
  340. tm.assert_numpy_array_equal(result, expected)
  341. def test_td_mul_numeric_ndarray_0d(self):
  342. td = Timedelta("1 day")
  343. other = np.array(2)
  344. assert other.ndim == 0
  345. expected = Timedelta("2 days")
  346. res = td * other
  347. assert type(res) is Timedelta
  348. assert res == expected
  349. res = other * td
  350. assert type(res) is Timedelta
  351. assert res == expected
  352. def test_td_mul_td64_ndarray_invalid(self):
  353. td = Timedelta("1 day")
  354. other = np.array([Timedelta("2 Days").to_timedelta64()])
  355. msg = (
  356. "ufunc '?multiply'? cannot use operands with types "
  357. rf"dtype\('{tm.ENDIAN}m8\[ns\]'\) and dtype\('{tm.ENDIAN}m8\[ns\]'\)"
  358. )
  359. with pytest.raises(TypeError, match=msg):
  360. td * other
  361. with pytest.raises(TypeError, match=msg):
  362. other * td
  363. # ---------------------------------------------------------------
  364. # Timedelta.__div__, __truediv__
  365. def test_td_div_timedeltalike_scalar(self):
  366. # GH#19738
  367. td = Timedelta(10, unit="d")
  368. result = td / offsets.Hour(1)
  369. assert result == 240
  370. assert td / td == 1
  371. assert td / np.timedelta64(60, "h") == 4
  372. assert np.isnan(td / NaT)
  373. def test_td_div_td64_non_nano(self):
  374. # truediv
  375. td = Timedelta("1 days 2 hours 3 ns")
  376. result = td / np.timedelta64(1, "D")
  377. assert result == td._value / (86400 * 10**9)
  378. result = td / np.timedelta64(1, "s")
  379. assert result == td._value / 10**9
  380. result = td / np.timedelta64(1, "ns")
  381. assert result == td._value
  382. # floordiv
  383. td = Timedelta("1 days 2 hours 3 ns")
  384. result = td // np.timedelta64(1, "D")
  385. assert result == 1
  386. result = td // np.timedelta64(1, "s")
  387. assert result == 93600
  388. result = td // np.timedelta64(1, "ns")
  389. assert result == td._value
  390. def test_td_div_numeric_scalar(self):
  391. # GH#19738
  392. td = Timedelta(10, unit="d")
  393. result = td / 2
  394. assert isinstance(result, Timedelta)
  395. assert result == Timedelta(days=5)
  396. result = td / 5
  397. assert isinstance(result, Timedelta)
  398. assert result == Timedelta(days=2)
  399. @pytest.mark.parametrize(
  400. "nan",
  401. [
  402. np.nan,
  403. np.float64("NaN"),
  404. float("nan"),
  405. ],
  406. )
  407. def test_td_div_nan(self, nan):
  408. # np.float64('NaN') has a 'dtype' attr, avoid treating as array
  409. td = Timedelta(10, unit="d")
  410. result = td / nan
  411. assert result is NaT
  412. result = td // nan
  413. assert result is NaT
  414. def test_td_div_td64_ndarray(self):
  415. td = Timedelta("1 day")
  416. other = np.array([Timedelta("2 Days").to_timedelta64()])
  417. expected = np.array([0.5])
  418. result = td / other
  419. tm.assert_numpy_array_equal(result, expected)
  420. result = other / td
  421. tm.assert_numpy_array_equal(result, expected * 4)
  422. def test_td_div_ndarray_0d(self):
  423. td = Timedelta("1 day")
  424. other = np.array(1)
  425. res = td / other
  426. assert isinstance(res, Timedelta)
  427. assert res == td
  428. # ---------------------------------------------------------------
  429. # Timedelta.__rdiv__
  430. def test_td_rdiv_timedeltalike_scalar(self):
  431. # GH#19738
  432. td = Timedelta(10, unit="d")
  433. result = offsets.Hour(1) / td
  434. assert result == 1 / 240.0
  435. assert np.timedelta64(60, "h") / td == 0.25
  436. def test_td_rdiv_na_scalar(self):
  437. # GH#31869 None gets cast to NaT
  438. td = Timedelta(10, unit="d")
  439. result = NaT / td
  440. assert np.isnan(result)
  441. result = None / td
  442. assert np.isnan(result)
  443. result = np.timedelta64("NaT") / td
  444. assert np.isnan(result)
  445. msg = r"unsupported operand type\(s\) for /: 'numpy.datetime64' and 'Timedelta'"
  446. with pytest.raises(TypeError, match=msg):
  447. np.datetime64("NaT") / td
  448. msg = r"unsupported operand type\(s\) for /: 'float' and 'Timedelta'"
  449. with pytest.raises(TypeError, match=msg):
  450. np.nan / td
  451. def test_td_rdiv_ndarray(self):
  452. td = Timedelta(10, unit="d")
  453. arr = np.array([td], dtype=object)
  454. result = arr / td
  455. expected = np.array([1], dtype=np.float64)
  456. tm.assert_numpy_array_equal(result, expected)
  457. arr = np.array([None])
  458. result = arr / td
  459. expected = np.array([np.nan])
  460. tm.assert_numpy_array_equal(result, expected)
  461. arr = np.array([np.nan], dtype=object)
  462. msg = r"unsupported operand type\(s\) for /: 'float' and 'Timedelta'"
  463. with pytest.raises(TypeError, match=msg):
  464. arr / td
  465. arr = np.array([np.nan], dtype=np.float64)
  466. msg = "cannot use operands with types dtype"
  467. with pytest.raises(TypeError, match=msg):
  468. arr / td
  469. def test_td_rdiv_ndarray_0d(self):
  470. td = Timedelta(10, unit="d")
  471. arr = np.array(td.asm8)
  472. assert arr / td == 1
  473. # ---------------------------------------------------------------
  474. # Timedelta.__floordiv__
  475. def test_td_floordiv_timedeltalike_scalar(self):
  476. # GH#18846
  477. td = Timedelta(hours=3, minutes=4)
  478. scalar = Timedelta(hours=3, minutes=3)
  479. assert td // scalar == 1
  480. assert -td // scalar.to_pytimedelta() == -2
  481. assert (2 * td) // scalar.to_timedelta64() == 2
  482. def test_td_floordiv_null_scalar(self):
  483. # GH#18846
  484. td = Timedelta(hours=3, minutes=4)
  485. assert td // np.nan is NaT
  486. assert np.isnan(td // NaT)
  487. assert np.isnan(td // np.timedelta64("NaT"))
  488. def test_td_floordiv_offsets(self):
  489. # GH#19738
  490. td = Timedelta(hours=3, minutes=4)
  491. assert td // offsets.Hour(1) == 3
  492. assert td // offsets.Minute(2) == 92
  493. def test_td_floordiv_invalid_scalar(self):
  494. # GH#18846
  495. td = Timedelta(hours=3, minutes=4)
  496. msg = "|".join(
  497. [
  498. r"Invalid dtype datetime64\[D\] for __floordiv__",
  499. "'dtype' is an invalid keyword argument for this function",
  500. r"ufunc '?floor_divide'? cannot use operands with types",
  501. ]
  502. )
  503. with pytest.raises(TypeError, match=msg):
  504. td // np.datetime64("2016-01-01", dtype="datetime64[us]")
  505. def test_td_floordiv_numeric_scalar(self):
  506. # GH#18846
  507. td = Timedelta(hours=3, minutes=4)
  508. expected = Timedelta(hours=1, minutes=32)
  509. assert td // 2 == expected
  510. assert td // 2.0 == expected
  511. assert td // np.float64(2.0) == expected
  512. assert td // np.int32(2.0) == expected
  513. assert td // np.uint8(2.0) == expected
  514. def test_td_floordiv_timedeltalike_array(self):
  515. # GH#18846
  516. td = Timedelta(hours=3, minutes=4)
  517. scalar = Timedelta(hours=3, minutes=3)
  518. # Array-like others
  519. assert td // np.array(scalar.to_timedelta64()) == 1
  520. res = (3 * td) // np.array([scalar.to_timedelta64()])
  521. expected = np.array([3], dtype=np.int64)
  522. tm.assert_numpy_array_equal(res, expected)
  523. res = (10 * td) // np.array([scalar.to_timedelta64(), np.timedelta64("NaT")])
  524. expected = np.array([10, np.nan])
  525. tm.assert_numpy_array_equal(res, expected)
  526. def test_td_floordiv_numeric_series(self):
  527. # GH#18846
  528. td = Timedelta(hours=3, minutes=4)
  529. ser = pd.Series([1], dtype=np.int64)
  530. res = td // ser
  531. assert res.dtype.kind == "m"
  532. # ---------------------------------------------------------------
  533. # Timedelta.__rfloordiv__
  534. def test_td_rfloordiv_timedeltalike_scalar(self):
  535. # GH#18846
  536. td = Timedelta(hours=3, minutes=3)
  537. scalar = Timedelta(hours=3, minutes=4)
  538. # scalar others
  539. # x // Timedelta is defined only for timedelta-like x. int-like,
  540. # float-like, and date-like, in particular, should all either
  541. # a) raise TypeError directly or
  542. # b) return NotImplemented, following which the reversed
  543. # operation will raise TypeError.
  544. assert td.__rfloordiv__(scalar) == 1
  545. assert (-td).__rfloordiv__(scalar.to_pytimedelta()) == -2
  546. assert (2 * td).__rfloordiv__(scalar.to_timedelta64()) == 0
  547. def test_td_rfloordiv_null_scalar(self):
  548. # GH#18846
  549. td = Timedelta(hours=3, minutes=3)
  550. assert np.isnan(td.__rfloordiv__(NaT))
  551. assert np.isnan(td.__rfloordiv__(np.timedelta64("NaT")))
  552. def test_td_rfloordiv_offsets(self):
  553. # GH#19738
  554. assert offsets.Hour(1) // Timedelta(minutes=25) == 2
  555. def test_td_rfloordiv_invalid_scalar(self):
  556. # GH#18846
  557. td = Timedelta(hours=3, minutes=3)
  558. dt64 = np.datetime64("2016-01-01", "us")
  559. assert td.__rfloordiv__(dt64) is NotImplemented
  560. msg = (
  561. r"unsupported operand type\(s\) for //: 'numpy.datetime64' and 'Timedelta'"
  562. )
  563. with pytest.raises(TypeError, match=msg):
  564. dt64 // td
  565. def test_td_rfloordiv_numeric_scalar(self):
  566. # GH#18846
  567. td = Timedelta(hours=3, minutes=3)
  568. assert td.__rfloordiv__(np.nan) is NotImplemented
  569. assert td.__rfloordiv__(3.5) is NotImplemented
  570. assert td.__rfloordiv__(2) is NotImplemented
  571. assert td.__rfloordiv__(np.float64(2.0)) is NotImplemented
  572. assert td.__rfloordiv__(np.uint8(9)) is NotImplemented
  573. assert td.__rfloordiv__(np.int32(2.0)) is NotImplemented
  574. msg = r"unsupported operand type\(s\) for //: '.*' and 'Timedelta"
  575. with pytest.raises(TypeError, match=msg):
  576. np.float64(2.0) // td
  577. with pytest.raises(TypeError, match=msg):
  578. np.uint8(9) // td
  579. with pytest.raises(TypeError, match=msg):
  580. # deprecated GH#19761, enforced GH#29797
  581. np.int32(2.0) // td
  582. def test_td_rfloordiv_timedeltalike_array(self):
  583. # GH#18846
  584. td = Timedelta(hours=3, minutes=3)
  585. scalar = Timedelta(hours=3, minutes=4)
  586. # Array-like others
  587. assert td.__rfloordiv__(np.array(scalar.to_timedelta64())) == 1
  588. res = td.__rfloordiv__(np.array([(3 * scalar).to_timedelta64()]))
  589. expected = np.array([3], dtype=np.int64)
  590. tm.assert_numpy_array_equal(res, expected)
  591. arr = np.array([(10 * scalar).to_timedelta64(), np.timedelta64("NaT")])
  592. res = td.__rfloordiv__(arr)
  593. expected = np.array([10, np.nan])
  594. tm.assert_numpy_array_equal(res, expected)
  595. def test_td_rfloordiv_intarray(self):
  596. # deprecated GH#19761, enforced GH#29797
  597. ints = np.array([1349654400, 1349740800, 1349827200, 1349913600]) * 10**9
  598. msg = "Invalid dtype"
  599. with pytest.raises(TypeError, match=msg):
  600. ints // Timedelta(1, unit="s")
  601. def test_td_rfloordiv_numeric_series(self):
  602. # GH#18846
  603. td = Timedelta(hours=3, minutes=3)
  604. ser = pd.Series([1], dtype=np.int64)
  605. res = td.__rfloordiv__(ser)
  606. assert res is NotImplemented
  607. msg = "Invalid dtype"
  608. with pytest.raises(TypeError, match=msg):
  609. # Deprecated GH#19761, enforced GH#29797
  610. ser // td
  611. # ----------------------------------------------------------------
  612. # Timedelta.__mod__, __rmod__
  613. def test_mod_timedeltalike(self):
  614. # GH#19365
  615. td = Timedelta(hours=37)
  616. # Timedelta-like others
  617. result = td % Timedelta(hours=6)
  618. assert isinstance(result, Timedelta)
  619. assert result == Timedelta(hours=1)
  620. result = td % timedelta(minutes=60)
  621. assert isinstance(result, Timedelta)
  622. assert result == Timedelta(0)
  623. result = td % NaT
  624. assert result is NaT
  625. def test_mod_timedelta64_nat(self):
  626. # GH#19365
  627. td = Timedelta(hours=37)
  628. result = td % np.timedelta64("NaT", "ns")
  629. assert result is NaT
  630. def test_mod_timedelta64(self):
  631. # GH#19365
  632. td = Timedelta(hours=37)
  633. result = td % np.timedelta64(2, "h")
  634. assert isinstance(result, Timedelta)
  635. assert result == Timedelta(hours=1)
  636. def test_mod_offset(self):
  637. # GH#19365
  638. td = Timedelta(hours=37)
  639. result = td % offsets.Hour(5)
  640. assert isinstance(result, Timedelta)
  641. assert result == Timedelta(hours=2)
  642. def test_mod_numeric(self):
  643. # GH#19365
  644. td = Timedelta(hours=37)
  645. # Numeric Others
  646. result = td % 2
  647. assert isinstance(result, Timedelta)
  648. assert result == Timedelta(0)
  649. result = td % 1e12
  650. assert isinstance(result, Timedelta)
  651. assert result == Timedelta(minutes=3, seconds=20)
  652. result = td % int(1e12)
  653. assert isinstance(result, Timedelta)
  654. assert result == Timedelta(minutes=3, seconds=20)
  655. def test_mod_invalid(self):
  656. # GH#19365
  657. td = Timedelta(hours=37)
  658. msg = "unsupported operand type"
  659. with pytest.raises(TypeError, match=msg):
  660. td % Timestamp("2018-01-22")
  661. with pytest.raises(TypeError, match=msg):
  662. td % []
  663. def test_rmod_pytimedelta(self):
  664. # GH#19365
  665. td = Timedelta(minutes=3)
  666. result = timedelta(minutes=4) % td
  667. assert isinstance(result, Timedelta)
  668. assert result == Timedelta(minutes=1)
  669. def test_rmod_timedelta64(self):
  670. # GH#19365
  671. td = Timedelta(minutes=3)
  672. result = np.timedelta64(5, "m") % td
  673. assert isinstance(result, Timedelta)
  674. assert result == Timedelta(minutes=2)
  675. def test_rmod_invalid(self):
  676. # GH#19365
  677. td = Timedelta(minutes=3)
  678. msg = "unsupported operand"
  679. with pytest.raises(TypeError, match=msg):
  680. Timestamp("2018-01-22") % td
  681. with pytest.raises(TypeError, match=msg):
  682. 15 % td
  683. with pytest.raises(TypeError, match=msg):
  684. 16.0 % td
  685. msg = "Invalid dtype int"
  686. with pytest.raises(TypeError, match=msg):
  687. np.array([22, 24]) % td
  688. # ----------------------------------------------------------------
  689. # Timedelta.__divmod__, __rdivmod__
  690. def test_divmod_numeric(self):
  691. # GH#19365
  692. td = Timedelta(days=2, hours=6)
  693. result = divmod(td, 53 * 3600 * 1e9)
  694. assert result[0] == Timedelta(1, unit="ns")
  695. assert isinstance(result[1], Timedelta)
  696. assert result[1] == Timedelta(hours=1)
  697. assert result
  698. result = divmod(td, np.nan)
  699. assert result[0] is NaT
  700. assert result[1] is NaT
  701. def test_divmod(self):
  702. # GH#19365
  703. td = Timedelta(days=2, hours=6)
  704. result = divmod(td, timedelta(days=1))
  705. assert result[0] == 2
  706. assert isinstance(result[1], Timedelta)
  707. assert result[1] == Timedelta(hours=6)
  708. result = divmod(td, 54)
  709. assert result[0] == Timedelta(hours=1)
  710. assert isinstance(result[1], Timedelta)
  711. assert result[1] == Timedelta(0)
  712. result = divmod(td, NaT)
  713. assert np.isnan(result[0])
  714. assert result[1] is NaT
  715. def test_divmod_offset(self):
  716. # GH#19365
  717. td = Timedelta(days=2, hours=6)
  718. result = divmod(td, offsets.Hour(-4))
  719. assert result[0] == -14
  720. assert isinstance(result[1], Timedelta)
  721. assert result[1] == Timedelta(hours=-2)
  722. def test_divmod_invalid(self):
  723. # GH#19365
  724. td = Timedelta(days=2, hours=6)
  725. msg = r"unsupported operand type\(s\) for //: 'Timedelta' and 'Timestamp'"
  726. with pytest.raises(TypeError, match=msg):
  727. divmod(td, Timestamp("2018-01-22"))
  728. def test_rdivmod_pytimedelta(self):
  729. # GH#19365
  730. result = divmod(timedelta(days=2, hours=6), Timedelta(days=1))
  731. assert result[0] == 2
  732. assert isinstance(result[1], Timedelta)
  733. assert result[1] == Timedelta(hours=6)
  734. def test_rdivmod_offset(self):
  735. result = divmod(offsets.Hour(54), Timedelta(hours=-4))
  736. assert result[0] == -14
  737. assert isinstance(result[1], Timedelta)
  738. assert result[1] == Timedelta(hours=-2)
  739. def test_rdivmod_invalid(self):
  740. # GH#19365
  741. td = Timedelta(minutes=3)
  742. msg = "unsupported operand type"
  743. with pytest.raises(TypeError, match=msg):
  744. divmod(Timestamp("2018-01-22"), td)
  745. with pytest.raises(TypeError, match=msg):
  746. divmod(15, td)
  747. with pytest.raises(TypeError, match=msg):
  748. divmod(16.0, td)
  749. msg = "Invalid dtype int"
  750. with pytest.raises(TypeError, match=msg):
  751. divmod(np.array([22, 24]), td)
  752. # ----------------------------------------------------------------
  753. @pytest.mark.parametrize(
  754. "op", [operator.mul, ops.rmul, operator.truediv, ops.rdiv, ops.rsub]
  755. )
  756. @pytest.mark.parametrize(
  757. "arr",
  758. [
  759. np.array([Timestamp("20130101 9:01"), Timestamp("20121230 9:02")]),
  760. np.array([Timestamp("2021-11-09 09:54:00"), Timedelta("1D")]),
  761. ],
  762. )
  763. def test_td_op_timedelta_timedeltalike_array(self, op, arr):
  764. msg = "unsupported operand type|cannot use operands with types"
  765. with pytest.raises(TypeError, match=msg):
  766. op(arr, Timedelta("1D"))
  767. class TestTimedeltaComparison:
  768. def test_compare_pytimedelta_bounds(self):
  769. # GH#49021 don't overflow on comparison with very large pytimedeltas
  770. for unit in ["ns", "us"]:
  771. tdmax = Timedelta.max.as_unit(unit).max
  772. tdmin = Timedelta.min.as_unit(unit).min
  773. assert tdmax < timedelta.max
  774. assert tdmax <= timedelta.max
  775. assert not tdmax > timedelta.max
  776. assert not tdmax >= timedelta.max
  777. assert tdmax != timedelta.max
  778. assert not tdmax == timedelta.max
  779. assert tdmin > timedelta.min
  780. assert tdmin >= timedelta.min
  781. assert not tdmin < timedelta.min
  782. assert not tdmin <= timedelta.min
  783. assert tdmin != timedelta.min
  784. assert not tdmin == timedelta.min
  785. # But the "ms" and "s"-reso bounds extend pass pytimedelta
  786. for unit in ["ms", "s"]:
  787. tdmax = Timedelta.max.as_unit(unit).max
  788. tdmin = Timedelta.min.as_unit(unit).min
  789. assert tdmax > timedelta.max
  790. assert tdmax >= timedelta.max
  791. assert not tdmax < timedelta.max
  792. assert not tdmax <= timedelta.max
  793. assert tdmax != timedelta.max
  794. assert not tdmax == timedelta.max
  795. assert tdmin < timedelta.min
  796. assert tdmin <= timedelta.min
  797. assert not tdmin > timedelta.min
  798. assert not tdmin >= timedelta.min
  799. assert tdmin != timedelta.min
  800. assert not tdmin == timedelta.min
  801. def test_compare_pytimedelta_bounds2(self):
  802. # a pytimedelta outside the microsecond bounds
  803. pytd = timedelta(days=999999999, seconds=86399)
  804. # NB: np.timedelta64(td, "s"") incorrectly overflows
  805. td64 = np.timedelta64(pytd.days, "D") + np.timedelta64(pytd.seconds, "s")
  806. td = Timedelta(td64)
  807. assert td.days == pytd.days
  808. assert td.seconds == pytd.seconds
  809. assert td == pytd
  810. assert not td != pytd
  811. assert not td < pytd
  812. assert not td > pytd
  813. assert td <= pytd
  814. assert td >= pytd
  815. td2 = td - Timedelta(seconds=1).as_unit("s")
  816. assert td2 != pytd
  817. assert not td2 == pytd
  818. assert td2 < pytd
  819. assert td2 <= pytd
  820. assert not td2 > pytd
  821. assert not td2 >= pytd
  822. def test_compare_tick(self, tick_classes):
  823. cls = tick_classes
  824. off = cls(4)
  825. td = off.delta
  826. assert isinstance(td, Timedelta)
  827. assert td == off
  828. assert not td != off
  829. assert td <= off
  830. assert td >= off
  831. assert not td < off
  832. assert not td > off
  833. assert not td == 2 * off
  834. assert td != 2 * off
  835. assert td <= 2 * off
  836. assert td < 2 * off
  837. assert not td >= 2 * off
  838. assert not td > 2 * off
  839. def test_comparison_object_array(self):
  840. # analogous to GH#15183
  841. td = Timedelta("2 days")
  842. other = Timedelta("3 hours")
  843. arr = np.array([other, td], dtype=object)
  844. res = arr == td
  845. expected = np.array([False, True], dtype=bool)
  846. assert (res == expected).all()
  847. # 2D case
  848. arr = np.array([[other, td], [td, other]], dtype=object)
  849. res = arr != td
  850. expected = np.array([[True, False], [False, True]], dtype=bool)
  851. assert res.shape == expected.shape
  852. assert (res == expected).all()
  853. def test_compare_timedelta_ndarray(self):
  854. # GH#11835
  855. periods = [Timedelta("0 days 01:00:00"), Timedelta("0 days 01:00:00")]
  856. arr = np.array(periods)
  857. result = arr[0] > arr
  858. expected = np.array([False, False])
  859. tm.assert_numpy_array_equal(result, expected)
  860. def test_compare_td64_ndarray(self):
  861. # GG#33441
  862. arr = np.arange(5).astype("timedelta64[ns]")
  863. td = Timedelta(arr[1])
  864. expected = np.array([False, True, False, False, False], dtype=bool)
  865. result = td == arr
  866. tm.assert_numpy_array_equal(result, expected)
  867. result = arr == td
  868. tm.assert_numpy_array_equal(result, expected)
  869. result = td != arr
  870. tm.assert_numpy_array_equal(result, ~expected)
  871. result = arr != td
  872. tm.assert_numpy_array_equal(result, ~expected)
  873. def test_compare_custom_object(self):
  874. """
  875. Make sure non supported operations on Timedelta returns NonImplemented
  876. and yields to other operand (GH#20829).
  877. """
  878. class CustomClass:
  879. def __init__(self, cmp_result=None) -> None:
  880. self.cmp_result = cmp_result
  881. def generic_result(self):
  882. if self.cmp_result is None:
  883. return NotImplemented
  884. else:
  885. return self.cmp_result
  886. def __eq__(self, other):
  887. return self.generic_result()
  888. def __gt__(self, other):
  889. return self.generic_result()
  890. t = Timedelta("1s")
  891. assert t != "string"
  892. assert t != 1
  893. assert t != CustomClass()
  894. assert t != CustomClass(cmp_result=False)
  895. assert t < CustomClass(cmp_result=True)
  896. assert not t < CustomClass(cmp_result=False)
  897. assert t == CustomClass(cmp_result=True)
  898. @pytest.mark.parametrize("val", ["string", 1])
  899. def test_compare_unknown_type(self, val):
  900. # GH#20829
  901. t = Timedelta("1s")
  902. msg = "not supported between instances of 'Timedelta' and '(int|str)'"
  903. with pytest.raises(TypeError, match=msg):
  904. t >= val
  905. with pytest.raises(TypeError, match=msg):
  906. t > val
  907. with pytest.raises(TypeError, match=msg):
  908. t <= val
  909. with pytest.raises(TypeError, match=msg):
  910. t < val
  911. def test_ops_notimplemented():
  912. class Other:
  913. pass
  914. other = Other()
  915. td = Timedelta("1 day")
  916. assert td.__add__(other) is NotImplemented
  917. assert td.__sub__(other) is NotImplemented
  918. assert td.__truediv__(other) is NotImplemented
  919. assert td.__mul__(other) is NotImplemented
  920. assert td.__floordiv__(other) is NotImplemented
  921. def test_ops_error_str():
  922. # GH#13624
  923. td = Timedelta("1 day")
  924. for left, right in [(td, "a"), ("a", td)]:
  925. msg = "|".join(
  926. [
  927. "unsupported operand type",
  928. r'can only concatenate str \(not "Timedelta"\) to str',
  929. "must be str, not Timedelta",
  930. ]
  931. )
  932. with pytest.raises(TypeError, match=msg):
  933. left + right
  934. msg = "not supported between instances of"
  935. with pytest.raises(TypeError, match=msg):
  936. left > right
  937. assert not left == right # pylint: disable=unneeded-not
  938. assert left != right