test_datetime64.py 88 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469
  1. # Arithmetic tests for DataFrame/Series/Index/Array classes that should
  2. # behave identically.
  3. # Specifically for datetime64 and datetime64tz dtypes
  4. from datetime import (
  5. datetime,
  6. time,
  7. timedelta,
  8. )
  9. from itertools import (
  10. product,
  11. starmap,
  12. )
  13. import operator
  14. import numpy as np
  15. import pytest
  16. import pytz
  17. from pandas._libs.tslibs.conversion import localize_pydatetime
  18. from pandas._libs.tslibs.offsets import shift_months
  19. from pandas.errors import PerformanceWarning
  20. import pandas as pd
  21. from pandas import (
  22. DateOffset,
  23. DatetimeIndex,
  24. NaT,
  25. Period,
  26. Series,
  27. Timedelta,
  28. TimedeltaIndex,
  29. Timestamp,
  30. date_range,
  31. )
  32. import pandas._testing as tm
  33. from pandas.core import roperator
  34. from pandas.tests.arithmetic.common import (
  35. assert_cannot_add,
  36. assert_invalid_addsub_type,
  37. assert_invalid_comparison,
  38. get_upcast_box,
  39. )
  40. # ------------------------------------------------------------------
  41. # Comparisons
  42. class TestDatetime64ArrayLikeComparisons:
  43. # Comparison tests for datetime64 vectors fully parametrized over
  44. # DataFrame/Series/DatetimeIndex/DatetimeArray. Ideally all comparison
  45. # tests will eventually end up here.
  46. def test_compare_zerodim(self, tz_naive_fixture, box_with_array):
  47. # Test comparison with zero-dimensional array is unboxed
  48. tz = tz_naive_fixture
  49. box = box_with_array
  50. dti = date_range("20130101", periods=3, tz=tz)
  51. other = np.array(dti.to_numpy()[0])
  52. dtarr = tm.box_expected(dti, box)
  53. xbox = get_upcast_box(dtarr, other, True)
  54. result = dtarr <= other
  55. expected = np.array([True, False, False])
  56. expected = tm.box_expected(expected, xbox)
  57. tm.assert_equal(result, expected)
  58. @pytest.mark.parametrize(
  59. "other",
  60. [
  61. "foo",
  62. -1,
  63. 99,
  64. 4.0,
  65. object(),
  66. timedelta(days=2),
  67. # GH#19800, GH#19301 datetime.date comparison raises to
  68. # match DatetimeIndex/Timestamp. This also matches the behavior
  69. # of stdlib datetime.datetime
  70. datetime(2001, 1, 1).date(),
  71. # GH#19301 None and NaN are *not* cast to NaT for comparisons
  72. None,
  73. np.nan,
  74. ],
  75. )
  76. def test_dt64arr_cmp_scalar_invalid(self, other, tz_naive_fixture, box_with_array):
  77. # GH#22074, GH#15966
  78. tz = tz_naive_fixture
  79. rng = date_range("1/1/2000", periods=10, tz=tz)
  80. dtarr = tm.box_expected(rng, box_with_array)
  81. assert_invalid_comparison(dtarr, other, box_with_array)
  82. @pytest.mark.parametrize(
  83. "other",
  84. [
  85. # GH#4968 invalid date/int comparisons
  86. list(range(10)),
  87. np.arange(10),
  88. np.arange(10).astype(np.float32),
  89. np.arange(10).astype(object),
  90. pd.timedelta_range("1ns", periods=10).array,
  91. np.array(pd.timedelta_range("1ns", periods=10)),
  92. list(pd.timedelta_range("1ns", periods=10)),
  93. pd.timedelta_range("1 Day", periods=10).astype(object),
  94. pd.period_range("1971-01-01", freq="D", periods=10).array,
  95. pd.period_range("1971-01-01", freq="D", periods=10).astype(object),
  96. ],
  97. )
  98. def test_dt64arr_cmp_arraylike_invalid(
  99. self, other, tz_naive_fixture, box_with_array
  100. ):
  101. tz = tz_naive_fixture
  102. dta = date_range("1970-01-01", freq="ns", periods=10, tz=tz)._data
  103. obj = tm.box_expected(dta, box_with_array)
  104. assert_invalid_comparison(obj, other, box_with_array)
  105. def test_dt64arr_cmp_mixed_invalid(self, tz_naive_fixture):
  106. tz = tz_naive_fixture
  107. dta = date_range("1970-01-01", freq="h", periods=5, tz=tz)._data
  108. other = np.array([0, 1, 2, dta[3], Timedelta(days=1)])
  109. result = dta == other
  110. expected = np.array([False, False, False, True, False])
  111. tm.assert_numpy_array_equal(result, expected)
  112. result = dta != other
  113. tm.assert_numpy_array_equal(result, ~expected)
  114. msg = "Invalid comparison between|Cannot compare type|not supported between"
  115. with pytest.raises(TypeError, match=msg):
  116. dta < other
  117. with pytest.raises(TypeError, match=msg):
  118. dta > other
  119. with pytest.raises(TypeError, match=msg):
  120. dta <= other
  121. with pytest.raises(TypeError, match=msg):
  122. dta >= other
  123. def test_dt64arr_nat_comparison(self, tz_naive_fixture, box_with_array):
  124. # GH#22242, GH#22163 DataFrame considered NaT == ts incorrectly
  125. tz = tz_naive_fixture
  126. box = box_with_array
  127. ts = Timestamp("2021-01-01", tz=tz)
  128. ser = Series([ts, NaT])
  129. obj = tm.box_expected(ser, box)
  130. xbox = get_upcast_box(obj, ts, True)
  131. expected = Series([True, False], dtype=np.bool_)
  132. expected = tm.box_expected(expected, xbox)
  133. result = obj == ts
  134. tm.assert_equal(result, expected)
  135. class TestDatetime64SeriesComparison:
  136. # TODO: moved from tests.series.test_operators; needs cleanup
  137. @pytest.mark.parametrize(
  138. "pair",
  139. [
  140. (
  141. [Timestamp("2011-01-01"), NaT, Timestamp("2011-01-03")],
  142. [NaT, NaT, Timestamp("2011-01-03")],
  143. ),
  144. (
  145. [Timedelta("1 days"), NaT, Timedelta("3 days")],
  146. [NaT, NaT, Timedelta("3 days")],
  147. ),
  148. (
  149. [Period("2011-01", freq="M"), NaT, Period("2011-03", freq="M")],
  150. [NaT, NaT, Period("2011-03", freq="M")],
  151. ),
  152. ],
  153. )
  154. @pytest.mark.parametrize("reverse", [True, False])
  155. @pytest.mark.parametrize("dtype", [None, object])
  156. @pytest.mark.parametrize(
  157. "op, expected",
  158. [
  159. (operator.eq, Series([False, False, True])),
  160. (operator.ne, Series([True, True, False])),
  161. (operator.lt, Series([False, False, False])),
  162. (operator.gt, Series([False, False, False])),
  163. (operator.ge, Series([False, False, True])),
  164. (operator.le, Series([False, False, True])),
  165. ],
  166. )
  167. def test_nat_comparisons(
  168. self,
  169. dtype,
  170. index_or_series,
  171. reverse,
  172. pair,
  173. op,
  174. expected,
  175. ):
  176. box = index_or_series
  177. lhs, rhs = pair
  178. if reverse:
  179. # add lhs / rhs switched data
  180. lhs, rhs = rhs, lhs
  181. left = Series(lhs, dtype=dtype)
  182. right = box(rhs, dtype=dtype)
  183. result = op(left, right)
  184. tm.assert_series_equal(result, expected)
  185. @pytest.mark.parametrize(
  186. "data",
  187. [
  188. [Timestamp("2011-01-01"), NaT, Timestamp("2011-01-03")],
  189. [Timedelta("1 days"), NaT, Timedelta("3 days")],
  190. [Period("2011-01", freq="M"), NaT, Period("2011-03", freq="M")],
  191. ],
  192. )
  193. @pytest.mark.parametrize("dtype", [None, object])
  194. def test_nat_comparisons_scalar(self, dtype, data, box_with_array):
  195. box = box_with_array
  196. left = Series(data, dtype=dtype)
  197. left = tm.box_expected(left, box)
  198. xbox = get_upcast_box(left, NaT, True)
  199. expected = [False, False, False]
  200. expected = tm.box_expected(expected, xbox)
  201. if box is pd.array and dtype is object:
  202. expected = pd.array(expected, dtype="bool")
  203. tm.assert_equal(left == NaT, expected)
  204. tm.assert_equal(NaT == left, expected)
  205. expected = [True, True, True]
  206. expected = tm.box_expected(expected, xbox)
  207. if box is pd.array and dtype is object:
  208. expected = pd.array(expected, dtype="bool")
  209. tm.assert_equal(left != NaT, expected)
  210. tm.assert_equal(NaT != left, expected)
  211. expected = [False, False, False]
  212. expected = tm.box_expected(expected, xbox)
  213. if box is pd.array and dtype is object:
  214. expected = pd.array(expected, dtype="bool")
  215. tm.assert_equal(left < NaT, expected)
  216. tm.assert_equal(NaT > left, expected)
  217. tm.assert_equal(left <= NaT, expected)
  218. tm.assert_equal(NaT >= left, expected)
  219. tm.assert_equal(left > NaT, expected)
  220. tm.assert_equal(NaT < left, expected)
  221. tm.assert_equal(left >= NaT, expected)
  222. tm.assert_equal(NaT <= left, expected)
  223. @pytest.mark.parametrize("val", [datetime(2000, 1, 4), datetime(2000, 1, 5)])
  224. def test_series_comparison_scalars(self, val):
  225. series = Series(date_range("1/1/2000", periods=10))
  226. result = series > val
  227. expected = Series([x > val for x in series])
  228. tm.assert_series_equal(result, expected)
  229. @pytest.mark.parametrize(
  230. "left,right", [("lt", "gt"), ("le", "ge"), ("eq", "eq"), ("ne", "ne")]
  231. )
  232. def test_timestamp_compare_series(self, left, right):
  233. # see gh-4982
  234. # Make sure we can compare Timestamps on the right AND left hand side.
  235. ser = Series(date_range("20010101", periods=10), name="dates")
  236. s_nat = ser.copy(deep=True)
  237. ser[0] = Timestamp("nat")
  238. ser[3] = Timestamp("nat")
  239. left_f = getattr(operator, left)
  240. right_f = getattr(operator, right)
  241. # No NaT
  242. expected = left_f(ser, Timestamp("20010109"))
  243. result = right_f(Timestamp("20010109"), ser)
  244. tm.assert_series_equal(result, expected)
  245. # NaT
  246. expected = left_f(ser, Timestamp("nat"))
  247. result = right_f(Timestamp("nat"), ser)
  248. tm.assert_series_equal(result, expected)
  249. # Compare to Timestamp with series containing NaT
  250. expected = left_f(s_nat, Timestamp("20010109"))
  251. result = right_f(Timestamp("20010109"), s_nat)
  252. tm.assert_series_equal(result, expected)
  253. # Compare to NaT with series containing NaT
  254. expected = left_f(s_nat, NaT)
  255. result = right_f(NaT, s_nat)
  256. tm.assert_series_equal(result, expected)
  257. def test_dt64arr_timestamp_equality(self, box_with_array):
  258. # GH#11034
  259. box = box_with_array
  260. ser = Series([Timestamp("2000-01-29 01:59:00"), Timestamp("2000-01-30"), NaT])
  261. ser = tm.box_expected(ser, box)
  262. xbox = get_upcast_box(ser, ser, True)
  263. result = ser != ser
  264. expected = tm.box_expected([False, False, True], xbox)
  265. tm.assert_equal(result, expected)
  266. if box is pd.DataFrame:
  267. # alignment for frame vs series comparisons deprecated
  268. # in GH#46795 enforced 2.0
  269. with pytest.raises(ValueError, match="not aligned"):
  270. ser != ser[0]
  271. else:
  272. result = ser != ser[0]
  273. expected = tm.box_expected([False, True, True], xbox)
  274. tm.assert_equal(result, expected)
  275. if box is pd.DataFrame:
  276. # alignment for frame vs series comparisons deprecated
  277. # in GH#46795 enforced 2.0
  278. with pytest.raises(ValueError, match="not aligned"):
  279. ser != ser[2]
  280. else:
  281. result = ser != ser[2]
  282. expected = tm.box_expected([True, True, True], xbox)
  283. tm.assert_equal(result, expected)
  284. result = ser == ser
  285. expected = tm.box_expected([True, True, False], xbox)
  286. tm.assert_equal(result, expected)
  287. if box is pd.DataFrame:
  288. # alignment for frame vs series comparisons deprecated
  289. # in GH#46795 enforced 2.0
  290. with pytest.raises(ValueError, match="not aligned"):
  291. ser == ser[0]
  292. else:
  293. result = ser == ser[0]
  294. expected = tm.box_expected([True, False, False], xbox)
  295. tm.assert_equal(result, expected)
  296. if box is pd.DataFrame:
  297. # alignment for frame vs series comparisons deprecated
  298. # in GH#46795 enforced 2.0
  299. with pytest.raises(ValueError, match="not aligned"):
  300. ser == ser[2]
  301. else:
  302. result = ser == ser[2]
  303. expected = tm.box_expected([False, False, False], xbox)
  304. tm.assert_equal(result, expected)
  305. @pytest.mark.parametrize(
  306. "datetimelike",
  307. [
  308. Timestamp("20130101"),
  309. datetime(2013, 1, 1),
  310. np.datetime64("2013-01-01T00:00", "ns"),
  311. ],
  312. )
  313. @pytest.mark.parametrize(
  314. "op,expected",
  315. [
  316. (operator.lt, [True, False, False, False]),
  317. (operator.le, [True, True, False, False]),
  318. (operator.eq, [False, True, False, False]),
  319. (operator.gt, [False, False, False, True]),
  320. ],
  321. )
  322. def test_dt64_compare_datetime_scalar(self, datetimelike, op, expected):
  323. # GH#17965, test for ability to compare datetime64[ns] columns
  324. # to datetimelike
  325. ser = Series(
  326. [
  327. Timestamp("20120101"),
  328. Timestamp("20130101"),
  329. np.nan,
  330. Timestamp("20130103"),
  331. ],
  332. name="A",
  333. )
  334. result = op(ser, datetimelike)
  335. expected = Series(expected, name="A")
  336. tm.assert_series_equal(result, expected)
  337. class TestDatetimeIndexComparisons:
  338. # TODO: moved from tests.indexes.test_base; parametrize and de-duplicate
  339. def test_comparators(self, comparison_op):
  340. index = date_range("2020-01-01", periods=10)
  341. element = index[len(index) // 2]
  342. element = Timestamp(element).to_datetime64()
  343. arr = np.array(index)
  344. arr_result = comparison_op(arr, element)
  345. index_result = comparison_op(index, element)
  346. assert isinstance(index_result, np.ndarray)
  347. tm.assert_numpy_array_equal(arr_result, index_result)
  348. @pytest.mark.parametrize(
  349. "other",
  350. [datetime(2016, 1, 1), Timestamp("2016-01-01"), np.datetime64("2016-01-01")],
  351. )
  352. def test_dti_cmp_datetimelike(self, other, tz_naive_fixture):
  353. tz = tz_naive_fixture
  354. dti = date_range("2016-01-01", periods=2, tz=tz)
  355. if tz is not None:
  356. if isinstance(other, np.datetime64):
  357. pytest.skip(f"{type(other).__name__} is not tz aware")
  358. other = localize_pydatetime(other, dti.tzinfo)
  359. result = dti == other
  360. expected = np.array([True, False])
  361. tm.assert_numpy_array_equal(result, expected)
  362. result = dti > other
  363. expected = np.array([False, True])
  364. tm.assert_numpy_array_equal(result, expected)
  365. result = dti >= other
  366. expected = np.array([True, True])
  367. tm.assert_numpy_array_equal(result, expected)
  368. result = dti < other
  369. expected = np.array([False, False])
  370. tm.assert_numpy_array_equal(result, expected)
  371. result = dti <= other
  372. expected = np.array([True, False])
  373. tm.assert_numpy_array_equal(result, expected)
  374. @pytest.mark.parametrize("dtype", [None, object])
  375. def test_dti_cmp_nat(self, dtype, box_with_array):
  376. left = DatetimeIndex([Timestamp("2011-01-01"), NaT, Timestamp("2011-01-03")])
  377. right = DatetimeIndex([NaT, NaT, Timestamp("2011-01-03")])
  378. left = tm.box_expected(left, box_with_array)
  379. right = tm.box_expected(right, box_with_array)
  380. xbox = get_upcast_box(left, right, True)
  381. lhs, rhs = left, right
  382. if dtype is object:
  383. lhs, rhs = left.astype(object), right.astype(object)
  384. result = rhs == lhs
  385. expected = np.array([False, False, True])
  386. expected = tm.box_expected(expected, xbox)
  387. tm.assert_equal(result, expected)
  388. result = lhs != rhs
  389. expected = np.array([True, True, False])
  390. expected = tm.box_expected(expected, xbox)
  391. tm.assert_equal(result, expected)
  392. expected = np.array([False, False, False])
  393. expected = tm.box_expected(expected, xbox)
  394. tm.assert_equal(lhs == NaT, expected)
  395. tm.assert_equal(NaT == rhs, expected)
  396. expected = np.array([True, True, True])
  397. expected = tm.box_expected(expected, xbox)
  398. tm.assert_equal(lhs != NaT, expected)
  399. tm.assert_equal(NaT != lhs, expected)
  400. expected = np.array([False, False, False])
  401. expected = tm.box_expected(expected, xbox)
  402. tm.assert_equal(lhs < NaT, expected)
  403. tm.assert_equal(NaT > lhs, expected)
  404. def test_dti_cmp_nat_behaves_like_float_cmp_nan(self):
  405. fidx1 = pd.Index([1.0, np.nan, 3.0, np.nan, 5.0, 7.0])
  406. fidx2 = pd.Index([2.0, 3.0, np.nan, np.nan, 6.0, 7.0])
  407. didx1 = DatetimeIndex(
  408. ["2014-01-01", NaT, "2014-03-01", NaT, "2014-05-01", "2014-07-01"]
  409. )
  410. didx2 = DatetimeIndex(
  411. ["2014-02-01", "2014-03-01", NaT, NaT, "2014-06-01", "2014-07-01"]
  412. )
  413. darr = np.array(
  414. [
  415. np.datetime64("2014-02-01 00:00"),
  416. np.datetime64("2014-03-01 00:00"),
  417. np.datetime64("nat"),
  418. np.datetime64("nat"),
  419. np.datetime64("2014-06-01 00:00"),
  420. np.datetime64("2014-07-01 00:00"),
  421. ]
  422. )
  423. cases = [(fidx1, fidx2), (didx1, didx2), (didx1, darr)]
  424. # Check pd.NaT is handles as the same as np.nan
  425. with tm.assert_produces_warning(None):
  426. for idx1, idx2 in cases:
  427. result = idx1 < idx2
  428. expected = np.array([True, False, False, False, True, False])
  429. tm.assert_numpy_array_equal(result, expected)
  430. result = idx2 > idx1
  431. expected = np.array([True, False, False, False, True, False])
  432. tm.assert_numpy_array_equal(result, expected)
  433. result = idx1 <= idx2
  434. expected = np.array([True, False, False, False, True, True])
  435. tm.assert_numpy_array_equal(result, expected)
  436. result = idx2 >= idx1
  437. expected = np.array([True, False, False, False, True, True])
  438. tm.assert_numpy_array_equal(result, expected)
  439. result = idx1 == idx2
  440. expected = np.array([False, False, False, False, False, True])
  441. tm.assert_numpy_array_equal(result, expected)
  442. result = idx1 != idx2
  443. expected = np.array([True, True, True, True, True, False])
  444. tm.assert_numpy_array_equal(result, expected)
  445. with tm.assert_produces_warning(None):
  446. for idx1, val in [(fidx1, np.nan), (didx1, NaT)]:
  447. result = idx1 < val
  448. expected = np.array([False, False, False, False, False, False])
  449. tm.assert_numpy_array_equal(result, expected)
  450. result = idx1 > val
  451. tm.assert_numpy_array_equal(result, expected)
  452. result = idx1 <= val
  453. tm.assert_numpy_array_equal(result, expected)
  454. result = idx1 >= val
  455. tm.assert_numpy_array_equal(result, expected)
  456. result = idx1 == val
  457. tm.assert_numpy_array_equal(result, expected)
  458. result = idx1 != val
  459. expected = np.array([True, True, True, True, True, True])
  460. tm.assert_numpy_array_equal(result, expected)
  461. # Check pd.NaT is handles as the same as np.nan
  462. with tm.assert_produces_warning(None):
  463. for idx1, val in [(fidx1, 3), (didx1, datetime(2014, 3, 1))]:
  464. result = idx1 < val
  465. expected = np.array([True, False, False, False, False, False])
  466. tm.assert_numpy_array_equal(result, expected)
  467. result = idx1 > val
  468. expected = np.array([False, False, False, False, True, True])
  469. tm.assert_numpy_array_equal(result, expected)
  470. result = idx1 <= val
  471. expected = np.array([True, False, True, False, False, False])
  472. tm.assert_numpy_array_equal(result, expected)
  473. result = idx1 >= val
  474. expected = np.array([False, False, True, False, True, True])
  475. tm.assert_numpy_array_equal(result, expected)
  476. result = idx1 == val
  477. expected = np.array([False, False, True, False, False, False])
  478. tm.assert_numpy_array_equal(result, expected)
  479. result = idx1 != val
  480. expected = np.array([True, True, False, True, True, True])
  481. tm.assert_numpy_array_equal(result, expected)
  482. def test_comparison_tzawareness_compat(self, comparison_op, box_with_array):
  483. # GH#18162
  484. op = comparison_op
  485. box = box_with_array
  486. dr = date_range("2016-01-01", periods=6)
  487. dz = dr.tz_localize("US/Pacific")
  488. dr = tm.box_expected(dr, box)
  489. dz = tm.box_expected(dz, box)
  490. if box is pd.DataFrame:
  491. tolist = lambda x: x.astype(object).values.tolist()[0]
  492. else:
  493. tolist = list
  494. if op not in [operator.eq, operator.ne]:
  495. msg = (
  496. r"Invalid comparison between dtype=datetime64\[ns.*\] "
  497. "and (Timestamp|DatetimeArray|list|ndarray)"
  498. )
  499. with pytest.raises(TypeError, match=msg):
  500. op(dr, dz)
  501. with pytest.raises(TypeError, match=msg):
  502. op(dr, tolist(dz))
  503. with pytest.raises(TypeError, match=msg):
  504. op(dr, np.array(tolist(dz), dtype=object))
  505. with pytest.raises(TypeError, match=msg):
  506. op(dz, dr)
  507. with pytest.raises(TypeError, match=msg):
  508. op(dz, tolist(dr))
  509. with pytest.raises(TypeError, match=msg):
  510. op(dz, np.array(tolist(dr), dtype=object))
  511. # The aware==aware and naive==naive comparisons should *not* raise
  512. assert np.all(dr == dr)
  513. assert np.all(dr == tolist(dr))
  514. assert np.all(tolist(dr) == dr)
  515. assert np.all(np.array(tolist(dr), dtype=object) == dr)
  516. assert np.all(dr == np.array(tolist(dr), dtype=object))
  517. assert np.all(dz == dz)
  518. assert np.all(dz == tolist(dz))
  519. assert np.all(tolist(dz) == dz)
  520. assert np.all(np.array(tolist(dz), dtype=object) == dz)
  521. assert np.all(dz == np.array(tolist(dz), dtype=object))
  522. def test_comparison_tzawareness_compat_scalars(self, comparison_op, box_with_array):
  523. # GH#18162
  524. op = comparison_op
  525. dr = date_range("2016-01-01", periods=6)
  526. dz = dr.tz_localize("US/Pacific")
  527. dr = tm.box_expected(dr, box_with_array)
  528. dz = tm.box_expected(dz, box_with_array)
  529. # Check comparisons against scalar Timestamps
  530. ts = Timestamp("2000-03-14 01:59")
  531. ts_tz = Timestamp("2000-03-14 01:59", tz="Europe/Amsterdam")
  532. assert np.all(dr > ts)
  533. msg = r"Invalid comparison between dtype=datetime64\[ns.*\] and Timestamp"
  534. if op not in [operator.eq, operator.ne]:
  535. with pytest.raises(TypeError, match=msg):
  536. op(dr, ts_tz)
  537. assert np.all(dz > ts_tz)
  538. if op not in [operator.eq, operator.ne]:
  539. with pytest.raises(TypeError, match=msg):
  540. op(dz, ts)
  541. if op not in [operator.eq, operator.ne]:
  542. # GH#12601: Check comparison against Timestamps and DatetimeIndex
  543. with pytest.raises(TypeError, match=msg):
  544. op(ts, dz)
  545. @pytest.mark.parametrize(
  546. "other",
  547. [datetime(2016, 1, 1), Timestamp("2016-01-01"), np.datetime64("2016-01-01")],
  548. )
  549. # Bug in NumPy? https://github.com/numpy/numpy/issues/13841
  550. # Raising in __eq__ will fallback to NumPy, which warns, fails,
  551. # then re-raises the original exception. So we just need to ignore.
  552. @pytest.mark.filterwarnings("ignore:elementwise comp:DeprecationWarning")
  553. def test_scalar_comparison_tzawareness(
  554. self, comparison_op, other, tz_aware_fixture, box_with_array
  555. ):
  556. op = comparison_op
  557. tz = tz_aware_fixture
  558. dti = date_range("2016-01-01", periods=2, tz=tz)
  559. dtarr = tm.box_expected(dti, box_with_array)
  560. xbox = get_upcast_box(dtarr, other, True)
  561. if op in [operator.eq, operator.ne]:
  562. exbool = op is operator.ne
  563. expected = np.array([exbool, exbool], dtype=bool)
  564. expected = tm.box_expected(expected, xbox)
  565. result = op(dtarr, other)
  566. tm.assert_equal(result, expected)
  567. result = op(other, dtarr)
  568. tm.assert_equal(result, expected)
  569. else:
  570. msg = (
  571. r"Invalid comparison between dtype=datetime64\[ns, .*\] "
  572. f"and {type(other).__name__}"
  573. )
  574. with pytest.raises(TypeError, match=msg):
  575. op(dtarr, other)
  576. with pytest.raises(TypeError, match=msg):
  577. op(other, dtarr)
  578. def test_nat_comparison_tzawareness(self, comparison_op):
  579. # GH#19276
  580. # tzaware DatetimeIndex should not raise when compared to NaT
  581. op = comparison_op
  582. dti = DatetimeIndex(
  583. ["2014-01-01", NaT, "2014-03-01", NaT, "2014-05-01", "2014-07-01"]
  584. )
  585. expected = np.array([op == operator.ne] * len(dti))
  586. result = op(dti, NaT)
  587. tm.assert_numpy_array_equal(result, expected)
  588. result = op(dti.tz_localize("US/Pacific"), NaT)
  589. tm.assert_numpy_array_equal(result, expected)
  590. def test_dti_cmp_str(self, tz_naive_fixture):
  591. # GH#22074
  592. # regardless of tz, we expect these comparisons are valid
  593. tz = tz_naive_fixture
  594. rng = date_range("1/1/2000", periods=10, tz=tz)
  595. other = "1/1/2000"
  596. result = rng == other
  597. expected = np.array([True] + [False] * 9)
  598. tm.assert_numpy_array_equal(result, expected)
  599. result = rng != other
  600. expected = np.array([False] + [True] * 9)
  601. tm.assert_numpy_array_equal(result, expected)
  602. result = rng < other
  603. expected = np.array([False] * 10)
  604. tm.assert_numpy_array_equal(result, expected)
  605. result = rng <= other
  606. expected = np.array([True] + [False] * 9)
  607. tm.assert_numpy_array_equal(result, expected)
  608. result = rng > other
  609. expected = np.array([False] + [True] * 9)
  610. tm.assert_numpy_array_equal(result, expected)
  611. result = rng >= other
  612. expected = np.array([True] * 10)
  613. tm.assert_numpy_array_equal(result, expected)
  614. def test_dti_cmp_list(self):
  615. rng = date_range("1/1/2000", periods=10)
  616. result = rng == list(rng)
  617. expected = rng == rng
  618. tm.assert_numpy_array_equal(result, expected)
  619. @pytest.mark.parametrize(
  620. "other",
  621. [
  622. pd.timedelta_range("1D", periods=10),
  623. pd.timedelta_range("1D", periods=10).to_series(),
  624. pd.timedelta_range("1D", periods=10).asi8.view("m8[ns]"),
  625. ],
  626. ids=lambda x: type(x).__name__,
  627. )
  628. def test_dti_cmp_tdi_tzawareness(self, other):
  629. # GH#22074
  630. # reversion test that we _don't_ call _assert_tzawareness_compat
  631. # when comparing against TimedeltaIndex
  632. dti = date_range("2000-01-01", periods=10, tz="Asia/Tokyo")
  633. result = dti == other
  634. expected = np.array([False] * 10)
  635. tm.assert_numpy_array_equal(result, expected)
  636. result = dti != other
  637. expected = np.array([True] * 10)
  638. tm.assert_numpy_array_equal(result, expected)
  639. msg = "Invalid comparison between"
  640. with pytest.raises(TypeError, match=msg):
  641. dti < other
  642. with pytest.raises(TypeError, match=msg):
  643. dti <= other
  644. with pytest.raises(TypeError, match=msg):
  645. dti > other
  646. with pytest.raises(TypeError, match=msg):
  647. dti >= other
  648. def test_dti_cmp_object_dtype(self):
  649. # GH#22074
  650. dti = date_range("2000-01-01", periods=10, tz="Asia/Tokyo")
  651. other = dti.astype("O")
  652. result = dti == other
  653. expected = np.array([True] * 10)
  654. tm.assert_numpy_array_equal(result, expected)
  655. other = dti.tz_localize(None)
  656. result = dti != other
  657. tm.assert_numpy_array_equal(result, expected)
  658. other = np.array(list(dti[:5]) + [Timedelta(days=1)] * 5)
  659. result = dti == other
  660. expected = np.array([True] * 5 + [False] * 5)
  661. tm.assert_numpy_array_equal(result, expected)
  662. msg = ">=' not supported between instances of 'Timestamp' and 'Timedelta'"
  663. with pytest.raises(TypeError, match=msg):
  664. dti >= other
  665. # ------------------------------------------------------------------
  666. # Arithmetic
  667. class TestDatetime64Arithmetic:
  668. # This class is intended for "finished" tests that are fully parametrized
  669. # over DataFrame/Series/Index/DatetimeArray
  670. # -------------------------------------------------------------
  671. # Addition/Subtraction of timedelta-like
  672. @pytest.mark.arm_slow
  673. def test_dt64arr_add_timedeltalike_scalar(
  674. self, tz_naive_fixture, two_hours, box_with_array
  675. ):
  676. # GH#22005, GH#22163 check DataFrame doesn't raise TypeError
  677. tz = tz_naive_fixture
  678. rng = date_range("2000-01-01", "2000-02-01", tz=tz)
  679. expected = date_range("2000-01-01 02:00", "2000-02-01 02:00", tz=tz)
  680. rng = tm.box_expected(rng, box_with_array)
  681. expected = tm.box_expected(expected, box_with_array)
  682. result = rng + two_hours
  683. tm.assert_equal(result, expected)
  684. result = two_hours + rng
  685. tm.assert_equal(result, expected)
  686. rng += two_hours
  687. tm.assert_equal(rng, expected)
  688. def test_dt64arr_sub_timedeltalike_scalar(
  689. self, tz_naive_fixture, two_hours, box_with_array
  690. ):
  691. tz = tz_naive_fixture
  692. rng = date_range("2000-01-01", "2000-02-01", tz=tz)
  693. expected = date_range("1999-12-31 22:00", "2000-01-31 22:00", tz=tz)
  694. rng = tm.box_expected(rng, box_with_array)
  695. expected = tm.box_expected(expected, box_with_array)
  696. result = rng - two_hours
  697. tm.assert_equal(result, expected)
  698. rng -= two_hours
  699. tm.assert_equal(rng, expected)
  700. def test_dt64_array_sub_dt_with_different_timezone(self, box_with_array):
  701. t1 = date_range("20130101", periods=3).tz_localize("US/Eastern")
  702. t1 = tm.box_expected(t1, box_with_array)
  703. t2 = Timestamp("20130101").tz_localize("CET")
  704. tnaive = Timestamp(20130101)
  705. result = t1 - t2
  706. expected = TimedeltaIndex(
  707. ["0 days 06:00:00", "1 days 06:00:00", "2 days 06:00:00"]
  708. )
  709. expected = tm.box_expected(expected, box_with_array)
  710. tm.assert_equal(result, expected)
  711. result = t2 - t1
  712. expected = TimedeltaIndex(
  713. ["-1 days +18:00:00", "-2 days +18:00:00", "-3 days +18:00:00"]
  714. )
  715. expected = tm.box_expected(expected, box_with_array)
  716. tm.assert_equal(result, expected)
  717. msg = "Cannot subtract tz-naive and tz-aware datetime-like objects"
  718. with pytest.raises(TypeError, match=msg):
  719. t1 - tnaive
  720. with pytest.raises(TypeError, match=msg):
  721. tnaive - t1
  722. def test_dt64_array_sub_dt64_array_with_different_timezone(self, box_with_array):
  723. t1 = date_range("20130101", periods=3).tz_localize("US/Eastern")
  724. t1 = tm.box_expected(t1, box_with_array)
  725. t2 = date_range("20130101", periods=3).tz_localize("CET")
  726. t2 = tm.box_expected(t2, box_with_array)
  727. tnaive = date_range("20130101", periods=3)
  728. result = t1 - t2
  729. expected = TimedeltaIndex(
  730. ["0 days 06:00:00", "0 days 06:00:00", "0 days 06:00:00"]
  731. )
  732. expected = tm.box_expected(expected, box_with_array)
  733. tm.assert_equal(result, expected)
  734. result = t2 - t1
  735. expected = TimedeltaIndex(
  736. ["-1 days +18:00:00", "-1 days +18:00:00", "-1 days +18:00:00"]
  737. )
  738. expected = tm.box_expected(expected, box_with_array)
  739. tm.assert_equal(result, expected)
  740. msg = "Cannot subtract tz-naive and tz-aware datetime-like objects"
  741. with pytest.raises(TypeError, match=msg):
  742. t1 - tnaive
  743. with pytest.raises(TypeError, match=msg):
  744. tnaive - t1
  745. def test_dt64arr_add_sub_td64_nat(self, box_with_array, tz_naive_fixture):
  746. # GH#23320 special handling for timedelta64("NaT")
  747. tz = tz_naive_fixture
  748. dti = date_range("1994-04-01", periods=9, tz=tz, freq="QS")
  749. other = np.timedelta64("NaT")
  750. expected = DatetimeIndex(["NaT"] * 9, tz=tz).as_unit("ns")
  751. obj = tm.box_expected(dti, box_with_array)
  752. expected = tm.box_expected(expected, box_with_array)
  753. result = obj + other
  754. tm.assert_equal(result, expected)
  755. result = other + obj
  756. tm.assert_equal(result, expected)
  757. result = obj - other
  758. tm.assert_equal(result, expected)
  759. msg = "cannot subtract"
  760. with pytest.raises(TypeError, match=msg):
  761. other - obj
  762. def test_dt64arr_add_sub_td64ndarray(self, tz_naive_fixture, box_with_array):
  763. tz = tz_naive_fixture
  764. dti = date_range("2016-01-01", periods=3, tz=tz)
  765. tdi = TimedeltaIndex(["-1 Day", "-1 Day", "-1 Day"])
  766. tdarr = tdi.values
  767. expected = date_range("2015-12-31", "2016-01-02", periods=3, tz=tz)
  768. dtarr = tm.box_expected(dti, box_with_array)
  769. expected = tm.box_expected(expected, box_with_array)
  770. result = dtarr + tdarr
  771. tm.assert_equal(result, expected)
  772. result = tdarr + dtarr
  773. tm.assert_equal(result, expected)
  774. expected = date_range("2016-01-02", "2016-01-04", periods=3, tz=tz)
  775. expected = tm.box_expected(expected, box_with_array)
  776. result = dtarr - tdarr
  777. tm.assert_equal(result, expected)
  778. msg = "cannot subtract|(bad|unsupported) operand type for unary"
  779. with pytest.raises(TypeError, match=msg):
  780. tdarr - dtarr
  781. # -----------------------------------------------------------------
  782. # Subtraction of datetime-like scalars
  783. @pytest.mark.parametrize(
  784. "ts",
  785. [
  786. Timestamp("2013-01-01"),
  787. Timestamp("2013-01-01").to_pydatetime(),
  788. Timestamp("2013-01-01").to_datetime64(),
  789. # GH#7996, GH#22163 ensure non-nano datetime64 is converted to nano
  790. # for DataFrame operation
  791. np.datetime64("2013-01-01", "D"),
  792. ],
  793. )
  794. def test_dt64arr_sub_dtscalar(self, box_with_array, ts):
  795. # GH#8554, GH#22163 DataFrame op should _not_ return dt64 dtype
  796. idx = date_range("2013-01-01", periods=3)._with_freq(None)
  797. idx = tm.box_expected(idx, box_with_array)
  798. expected = TimedeltaIndex(["0 Days", "1 Day", "2 Days"])
  799. expected = tm.box_expected(expected, box_with_array)
  800. result = idx - ts
  801. tm.assert_equal(result, expected)
  802. result = ts - idx
  803. tm.assert_equal(result, -expected)
  804. tm.assert_equal(result, -expected)
  805. def test_dt64arr_sub_timestamp_tzaware(self, box_with_array):
  806. ser = date_range("2014-03-17", periods=2, freq="D", tz="US/Eastern")
  807. ser = ser._with_freq(None)
  808. ts = ser[0]
  809. ser = tm.box_expected(ser, box_with_array)
  810. delta_series = Series([np.timedelta64(0, "D"), np.timedelta64(1, "D")])
  811. expected = tm.box_expected(delta_series, box_with_array)
  812. tm.assert_equal(ser - ts, expected)
  813. tm.assert_equal(ts - ser, -expected)
  814. def test_dt64arr_sub_NaT(self, box_with_array, unit):
  815. # GH#18808
  816. dti = DatetimeIndex([NaT, Timestamp("19900315")]).as_unit(unit)
  817. ser = tm.box_expected(dti, box_with_array)
  818. result = ser - NaT
  819. expected = Series([NaT, NaT], dtype=f"timedelta64[{unit}]")
  820. expected = tm.box_expected(expected, box_with_array)
  821. tm.assert_equal(result, expected)
  822. dti_tz = dti.tz_localize("Asia/Tokyo")
  823. ser_tz = tm.box_expected(dti_tz, box_with_array)
  824. result = ser_tz - NaT
  825. expected = Series([NaT, NaT], dtype=f"timedelta64[{unit}]")
  826. expected = tm.box_expected(expected, box_with_array)
  827. tm.assert_equal(result, expected)
  828. # -------------------------------------------------------------
  829. # Subtraction of datetime-like array-like
  830. def test_dt64arr_sub_dt64object_array(self, box_with_array, tz_naive_fixture):
  831. dti = date_range("2016-01-01", periods=3, tz=tz_naive_fixture)
  832. expected = dti - dti
  833. obj = tm.box_expected(dti, box_with_array)
  834. expected = tm.box_expected(expected, box_with_array).astype(object)
  835. with tm.assert_produces_warning(PerformanceWarning):
  836. result = obj - obj.astype(object)
  837. tm.assert_equal(result, expected)
  838. def test_dt64arr_naive_sub_dt64ndarray(self, box_with_array):
  839. dti = date_range("2016-01-01", periods=3, tz=None)
  840. dt64vals = dti.values
  841. dtarr = tm.box_expected(dti, box_with_array)
  842. expected = dtarr - dtarr
  843. result = dtarr - dt64vals
  844. tm.assert_equal(result, expected)
  845. result = dt64vals - dtarr
  846. tm.assert_equal(result, expected)
  847. def test_dt64arr_aware_sub_dt64ndarray_raises(
  848. self, tz_aware_fixture, box_with_array
  849. ):
  850. tz = tz_aware_fixture
  851. dti = date_range("2016-01-01", periods=3, tz=tz)
  852. dt64vals = dti.values
  853. dtarr = tm.box_expected(dti, box_with_array)
  854. msg = "Cannot subtract tz-naive and tz-aware datetime"
  855. with pytest.raises(TypeError, match=msg):
  856. dtarr - dt64vals
  857. with pytest.raises(TypeError, match=msg):
  858. dt64vals - dtarr
  859. # -------------------------------------------------------------
  860. # Addition of datetime-like others (invalid)
  861. def test_dt64arr_add_dtlike_raises(self, tz_naive_fixture, box_with_array):
  862. # GH#22163 ensure DataFrame doesn't cast Timestamp to i8
  863. # GH#9631
  864. tz = tz_naive_fixture
  865. dti = date_range("2016-01-01", periods=3, tz=tz)
  866. if tz is None:
  867. dti2 = dti.tz_localize("US/Eastern")
  868. else:
  869. dti2 = dti.tz_localize(None)
  870. dtarr = tm.box_expected(dti, box_with_array)
  871. assert_cannot_add(dtarr, dti.values)
  872. assert_cannot_add(dtarr, dti)
  873. assert_cannot_add(dtarr, dtarr)
  874. assert_cannot_add(dtarr, dti[0])
  875. assert_cannot_add(dtarr, dti[0].to_pydatetime())
  876. assert_cannot_add(dtarr, dti[0].to_datetime64())
  877. assert_cannot_add(dtarr, dti2[0])
  878. assert_cannot_add(dtarr, dti2[0].to_pydatetime())
  879. assert_cannot_add(dtarr, np.datetime64("2011-01-01", "D"))
  880. # -------------------------------------------------------------
  881. # Other Invalid Addition/Subtraction
  882. # Note: freq here includes both Tick and non-Tick offsets; this is
  883. # relevant because historically integer-addition was allowed if we had
  884. # a freq.
  885. @pytest.mark.parametrize("freq", ["h", "D", "W", "2ME", "MS", "QE", "B", None])
  886. @pytest.mark.parametrize("dtype", [None, "uint8"])
  887. def test_dt64arr_addsub_intlike(
  888. self, request, dtype, index_or_series_or_array, freq, tz_naive_fixture
  889. ):
  890. # GH#19959, GH#19123, GH#19012
  891. # GH#55860 use index_or_series_or_array instead of box_with_array
  892. # bc DataFrame alignment makes it inapplicable
  893. tz = tz_naive_fixture
  894. if freq is None:
  895. dti = DatetimeIndex(["NaT", "2017-04-05 06:07:08"], tz=tz)
  896. else:
  897. dti = date_range("2016-01-01", periods=2, freq=freq, tz=tz)
  898. obj = index_or_series_or_array(dti)
  899. other = np.array([4, -1])
  900. if dtype is not None:
  901. other = other.astype(dtype)
  902. msg = "|".join(
  903. [
  904. "Addition/subtraction of integers",
  905. "cannot subtract DatetimeArray from",
  906. # IntegerArray
  907. "can only perform ops with numeric values",
  908. "unsupported operand type.*Categorical",
  909. r"unsupported operand type\(s\) for -: 'int' and 'Timestamp'",
  910. ]
  911. )
  912. assert_invalid_addsub_type(obj, 1, msg)
  913. assert_invalid_addsub_type(obj, np.int64(2), msg)
  914. assert_invalid_addsub_type(obj, np.array(3, dtype=np.int64), msg)
  915. assert_invalid_addsub_type(obj, other, msg)
  916. assert_invalid_addsub_type(obj, np.array(other), msg)
  917. assert_invalid_addsub_type(obj, pd.array(other), msg)
  918. assert_invalid_addsub_type(obj, pd.Categorical(other), msg)
  919. assert_invalid_addsub_type(obj, pd.Index(other), msg)
  920. assert_invalid_addsub_type(obj, Series(other), msg)
  921. @pytest.mark.parametrize(
  922. "other",
  923. [
  924. 3.14,
  925. np.array([2.0, 3.0]),
  926. # GH#13078 datetime +/- Period is invalid
  927. Period("2011-01-01", freq="D"),
  928. # https://github.com/pandas-dev/pandas/issues/10329
  929. time(1, 2, 3),
  930. ],
  931. )
  932. @pytest.mark.parametrize("dti_freq", [None, "D"])
  933. def test_dt64arr_add_sub_invalid(self, dti_freq, other, box_with_array):
  934. dti = DatetimeIndex(["2011-01-01", "2011-01-02"], freq=dti_freq)
  935. dtarr = tm.box_expected(dti, box_with_array)
  936. msg = "|".join(
  937. [
  938. "unsupported operand type",
  939. "cannot (add|subtract)",
  940. "cannot use operands with types",
  941. "ufunc '?(add|subtract)'? cannot use operands with types",
  942. "Concatenation operation is not implemented for NumPy arrays",
  943. ]
  944. )
  945. assert_invalid_addsub_type(dtarr, other, msg)
  946. @pytest.mark.parametrize("pi_freq", ["D", "W", "Q", "h"])
  947. @pytest.mark.parametrize("dti_freq", [None, "D"])
  948. def test_dt64arr_add_sub_parr(
  949. self, dti_freq, pi_freq, box_with_array, box_with_array2
  950. ):
  951. # GH#20049 subtracting PeriodIndex should raise TypeError
  952. dti = DatetimeIndex(["2011-01-01", "2011-01-02"], freq=dti_freq)
  953. pi = dti.to_period(pi_freq)
  954. dtarr = tm.box_expected(dti, box_with_array)
  955. parr = tm.box_expected(pi, box_with_array2)
  956. msg = "|".join(
  957. [
  958. "cannot (add|subtract)",
  959. "unsupported operand",
  960. "descriptor.*requires",
  961. "ufunc.*cannot use operands",
  962. ]
  963. )
  964. assert_invalid_addsub_type(dtarr, parr, msg)
  965. @pytest.mark.filterwarnings("ignore::pandas.errors.PerformanceWarning")
  966. def test_dt64arr_addsub_time_objects_raises(self, box_with_array, tz_naive_fixture):
  967. # https://github.com/pandas-dev/pandas/issues/10329
  968. tz = tz_naive_fixture
  969. obj1 = date_range("2012-01-01", periods=3, tz=tz)
  970. obj2 = [time(i, i, i) for i in range(3)]
  971. obj1 = tm.box_expected(obj1, box_with_array)
  972. obj2 = tm.box_expected(obj2, box_with_array)
  973. msg = "|".join(
  974. [
  975. "unsupported operand",
  976. "cannot subtract DatetimeArray from ndarray",
  977. ]
  978. )
  979. # pandas.errors.PerformanceWarning: Non-vectorized DateOffset being
  980. # applied to Series or DatetimeIndex
  981. # we aren't testing that here, so ignore.
  982. assert_invalid_addsub_type(obj1, obj2, msg=msg)
  983. # -------------------------------------------------------------
  984. # Other invalid operations
  985. @pytest.mark.parametrize(
  986. "dt64_series",
  987. [
  988. Series([Timestamp("19900315"), Timestamp("19900315")]),
  989. Series([NaT, Timestamp("19900315")]),
  990. Series([NaT, NaT], dtype="datetime64[ns]"),
  991. ],
  992. )
  993. @pytest.mark.parametrize("one", [1, 1.0, np.array(1)])
  994. def test_dt64_mul_div_numeric_invalid(self, one, dt64_series, box_with_array):
  995. obj = tm.box_expected(dt64_series, box_with_array)
  996. msg = "cannot perform .* with this index type"
  997. # multiplication
  998. with pytest.raises(TypeError, match=msg):
  999. obj * one
  1000. with pytest.raises(TypeError, match=msg):
  1001. one * obj
  1002. # division
  1003. with pytest.raises(TypeError, match=msg):
  1004. obj / one
  1005. with pytest.raises(TypeError, match=msg):
  1006. one / obj
  1007. class TestDatetime64DateOffsetArithmetic:
  1008. # -------------------------------------------------------------
  1009. # Tick DateOffsets
  1010. # TODO: parametrize over timezone?
  1011. @pytest.mark.parametrize("unit", ["s", "ms", "us", "ns"])
  1012. def test_dt64arr_series_add_tick_DateOffset(self, box_with_array, unit):
  1013. # GH#4532
  1014. # operate with pd.offsets
  1015. ser = Series(
  1016. [Timestamp("20130101 9:01"), Timestamp("20130101 9:02")]
  1017. ).dt.as_unit(unit)
  1018. expected = Series(
  1019. [Timestamp("20130101 9:01:05"), Timestamp("20130101 9:02:05")]
  1020. ).dt.as_unit(unit)
  1021. ser = tm.box_expected(ser, box_with_array)
  1022. expected = tm.box_expected(expected, box_with_array)
  1023. result = ser + pd.offsets.Second(5)
  1024. tm.assert_equal(result, expected)
  1025. result2 = pd.offsets.Second(5) + ser
  1026. tm.assert_equal(result2, expected)
  1027. def test_dt64arr_series_sub_tick_DateOffset(self, box_with_array):
  1028. # GH#4532
  1029. # operate with pd.offsets
  1030. ser = Series([Timestamp("20130101 9:01"), Timestamp("20130101 9:02")])
  1031. expected = Series(
  1032. [Timestamp("20130101 9:00:55"), Timestamp("20130101 9:01:55")]
  1033. )
  1034. ser = tm.box_expected(ser, box_with_array)
  1035. expected = tm.box_expected(expected, box_with_array)
  1036. result = ser - pd.offsets.Second(5)
  1037. tm.assert_equal(result, expected)
  1038. result2 = -pd.offsets.Second(5) + ser
  1039. tm.assert_equal(result2, expected)
  1040. msg = "(bad|unsupported) operand type for unary"
  1041. with pytest.raises(TypeError, match=msg):
  1042. pd.offsets.Second(5) - ser
  1043. @pytest.mark.parametrize(
  1044. "cls_name", ["Day", "Hour", "Minute", "Second", "Milli", "Micro", "Nano"]
  1045. )
  1046. def test_dt64arr_add_sub_tick_DateOffset_smoke(self, cls_name, box_with_array):
  1047. # GH#4532
  1048. # smoke tests for valid DateOffsets
  1049. ser = Series([Timestamp("20130101 9:01"), Timestamp("20130101 9:02")])
  1050. ser = tm.box_expected(ser, box_with_array)
  1051. offset_cls = getattr(pd.offsets, cls_name)
  1052. ser + offset_cls(5)
  1053. offset_cls(5) + ser
  1054. ser - offset_cls(5)
  1055. def test_dti_add_tick_tzaware(self, tz_aware_fixture, box_with_array):
  1056. # GH#21610, GH#22163 ensure DataFrame doesn't return object-dtype
  1057. tz = tz_aware_fixture
  1058. if tz == "US/Pacific":
  1059. dates = date_range("2012-11-01", periods=3, tz=tz)
  1060. offset = dates + pd.offsets.Hour(5)
  1061. assert dates[0] + pd.offsets.Hour(5) == offset[0]
  1062. dates = date_range("2010-11-01 00:00", periods=3, tz=tz, freq="h")
  1063. expected = DatetimeIndex(
  1064. ["2010-11-01 05:00", "2010-11-01 06:00", "2010-11-01 07:00"],
  1065. freq="h",
  1066. tz=tz,
  1067. ).as_unit("ns")
  1068. dates = tm.box_expected(dates, box_with_array)
  1069. expected = tm.box_expected(expected, box_with_array)
  1070. for scalar in [pd.offsets.Hour(5), np.timedelta64(5, "h"), timedelta(hours=5)]:
  1071. offset = dates + scalar
  1072. tm.assert_equal(offset, expected)
  1073. offset = scalar + dates
  1074. tm.assert_equal(offset, expected)
  1075. roundtrip = offset - scalar
  1076. tm.assert_equal(roundtrip, dates)
  1077. msg = "|".join(
  1078. ["bad operand type for unary -", "cannot subtract DatetimeArray"]
  1079. )
  1080. with pytest.raises(TypeError, match=msg):
  1081. scalar - dates
  1082. # -------------------------------------------------------------
  1083. # RelativeDelta DateOffsets
  1084. @pytest.mark.parametrize("unit", ["s", "ms", "us", "ns"])
  1085. def test_dt64arr_add_sub_relativedelta_offsets(self, box_with_array, unit):
  1086. # GH#10699
  1087. vec = DatetimeIndex(
  1088. [
  1089. Timestamp("2000-01-05 00:15:00"),
  1090. Timestamp("2000-01-31 00:23:00"),
  1091. Timestamp("2000-01-01"),
  1092. Timestamp("2000-03-31"),
  1093. Timestamp("2000-02-29"),
  1094. Timestamp("2000-12-31"),
  1095. Timestamp("2000-05-15"),
  1096. Timestamp("2001-06-15"),
  1097. ]
  1098. ).as_unit(unit)
  1099. vec = tm.box_expected(vec, box_with_array)
  1100. vec_items = vec.iloc[0] if box_with_array is pd.DataFrame else vec
  1101. # DateOffset relativedelta fastpath
  1102. relative_kwargs = [
  1103. ("years", 2),
  1104. ("months", 5),
  1105. ("days", 3),
  1106. ("hours", 5),
  1107. ("minutes", 10),
  1108. ("seconds", 2),
  1109. ("microseconds", 5),
  1110. ]
  1111. for i, (offset_unit, value) in enumerate(relative_kwargs):
  1112. off = DateOffset(**{offset_unit: value})
  1113. exp_unit = unit
  1114. if offset_unit == "microseconds" and unit != "ns":
  1115. exp_unit = "us"
  1116. # TODO(GH#55564): as_unit will be unnecessary
  1117. expected = DatetimeIndex([x + off for x in vec_items]).as_unit(exp_unit)
  1118. expected = tm.box_expected(expected, box_with_array)
  1119. tm.assert_equal(expected, vec + off)
  1120. expected = DatetimeIndex([x - off for x in vec_items]).as_unit(exp_unit)
  1121. expected = tm.box_expected(expected, box_with_array)
  1122. tm.assert_equal(expected, vec - off)
  1123. off = DateOffset(**dict(relative_kwargs[: i + 1]))
  1124. expected = DatetimeIndex([x + off for x in vec_items]).as_unit(exp_unit)
  1125. expected = tm.box_expected(expected, box_with_array)
  1126. tm.assert_equal(expected, vec + off)
  1127. expected = DatetimeIndex([x - off for x in vec_items]).as_unit(exp_unit)
  1128. expected = tm.box_expected(expected, box_with_array)
  1129. tm.assert_equal(expected, vec - off)
  1130. msg = "(bad|unsupported) operand type for unary"
  1131. with pytest.raises(TypeError, match=msg):
  1132. off - vec
  1133. # -------------------------------------------------------------
  1134. # Non-Tick, Non-RelativeDelta DateOffsets
  1135. # TODO: redundant with test_dt64arr_add_sub_DateOffset? that includes
  1136. # tz-aware cases which this does not
  1137. @pytest.mark.filterwarnings("ignore::pandas.errors.PerformanceWarning")
  1138. @pytest.mark.parametrize(
  1139. "cls_and_kwargs",
  1140. [
  1141. "YearBegin",
  1142. ("YearBegin", {"month": 5}),
  1143. "YearEnd",
  1144. ("YearEnd", {"month": 5}),
  1145. "MonthBegin",
  1146. "MonthEnd",
  1147. "SemiMonthEnd",
  1148. "SemiMonthBegin",
  1149. "Week",
  1150. ("Week", {"weekday": 3}),
  1151. "Week",
  1152. ("Week", {"weekday": 6}),
  1153. "BusinessDay",
  1154. "BDay",
  1155. "QuarterEnd",
  1156. "QuarterBegin",
  1157. "CustomBusinessDay",
  1158. "CDay",
  1159. "CBMonthEnd",
  1160. "CBMonthBegin",
  1161. "BMonthBegin",
  1162. "BMonthEnd",
  1163. "BusinessHour",
  1164. "BYearBegin",
  1165. "BYearEnd",
  1166. "BQuarterBegin",
  1167. ("LastWeekOfMonth", {"weekday": 2}),
  1168. (
  1169. "FY5253Quarter",
  1170. {
  1171. "qtr_with_extra_week": 1,
  1172. "startingMonth": 1,
  1173. "weekday": 2,
  1174. "variation": "nearest",
  1175. },
  1176. ),
  1177. ("FY5253", {"weekday": 0, "startingMonth": 2, "variation": "nearest"}),
  1178. ("WeekOfMonth", {"weekday": 2, "week": 2}),
  1179. "Easter",
  1180. ("DateOffset", {"day": 4}),
  1181. ("DateOffset", {"month": 5}),
  1182. ],
  1183. )
  1184. @pytest.mark.parametrize("normalize", [True, False])
  1185. @pytest.mark.parametrize("n", [0, 5])
  1186. @pytest.mark.parametrize("unit", ["s", "ms", "us", "ns"])
  1187. @pytest.mark.parametrize("tz", [None, "US/Central"])
  1188. def test_dt64arr_add_sub_DateOffsets(
  1189. self, box_with_array, n, normalize, cls_and_kwargs, unit, tz
  1190. ):
  1191. # GH#10699
  1192. # assert vectorized operation matches pointwise operations
  1193. if isinstance(cls_and_kwargs, tuple):
  1194. # If cls_name param is a tuple, then 2nd entry is kwargs for
  1195. # the offset constructor
  1196. cls_name, kwargs = cls_and_kwargs
  1197. else:
  1198. cls_name = cls_and_kwargs
  1199. kwargs = {}
  1200. if n == 0 and cls_name in [
  1201. "WeekOfMonth",
  1202. "LastWeekOfMonth",
  1203. "FY5253Quarter",
  1204. "FY5253",
  1205. ]:
  1206. # passing n = 0 is invalid for these offset classes
  1207. return
  1208. vec = (
  1209. DatetimeIndex(
  1210. [
  1211. Timestamp("2000-01-05 00:15:00"),
  1212. Timestamp("2000-01-31 00:23:00"),
  1213. Timestamp("2000-01-01"),
  1214. Timestamp("2000-03-31"),
  1215. Timestamp("2000-02-29"),
  1216. Timestamp("2000-12-31"),
  1217. Timestamp("2000-05-15"),
  1218. Timestamp("2001-06-15"),
  1219. ]
  1220. )
  1221. .as_unit(unit)
  1222. .tz_localize(tz)
  1223. )
  1224. vec = tm.box_expected(vec, box_with_array)
  1225. vec_items = vec.iloc[0] if box_with_array is pd.DataFrame else vec
  1226. offset_cls = getattr(pd.offsets, cls_name)
  1227. offset = offset_cls(n, normalize=normalize, **kwargs)
  1228. # TODO(GH#55564): as_unit will be unnecessary
  1229. expected = DatetimeIndex([x + offset for x in vec_items]).as_unit(unit)
  1230. expected = tm.box_expected(expected, box_with_array)
  1231. tm.assert_equal(expected, vec + offset)
  1232. tm.assert_equal(expected, offset + vec)
  1233. expected = DatetimeIndex([x - offset for x in vec_items]).as_unit(unit)
  1234. expected = tm.box_expected(expected, box_with_array)
  1235. tm.assert_equal(expected, vec - offset)
  1236. expected = DatetimeIndex([offset + x for x in vec_items]).as_unit(unit)
  1237. expected = tm.box_expected(expected, box_with_array)
  1238. tm.assert_equal(expected, offset + vec)
  1239. msg = "(bad|unsupported) operand type for unary"
  1240. with pytest.raises(TypeError, match=msg):
  1241. offset - vec
  1242. @pytest.mark.parametrize(
  1243. "other",
  1244. [
  1245. np.array([pd.offsets.MonthEnd(), pd.offsets.Day(n=2)]),
  1246. np.array([pd.offsets.DateOffset(years=1), pd.offsets.MonthEnd()]),
  1247. np.array( # matching offsets
  1248. [pd.offsets.DateOffset(years=1), pd.offsets.DateOffset(years=1)]
  1249. ),
  1250. ],
  1251. )
  1252. @pytest.mark.parametrize("op", [operator.add, roperator.radd, operator.sub])
  1253. def test_dt64arr_add_sub_offset_array(
  1254. self, tz_naive_fixture, box_with_array, op, other
  1255. ):
  1256. # GH#18849
  1257. # GH#10699 array of offsets
  1258. tz = tz_naive_fixture
  1259. dti = date_range("2017-01-01", periods=2, tz=tz)
  1260. dtarr = tm.box_expected(dti, box_with_array)
  1261. expected = DatetimeIndex([op(dti[n], other[n]) for n in range(len(dti))])
  1262. expected = tm.box_expected(expected, box_with_array).astype(object)
  1263. with tm.assert_produces_warning(PerformanceWarning):
  1264. res = op(dtarr, other)
  1265. tm.assert_equal(res, expected)
  1266. # Same thing but boxing other
  1267. other = tm.box_expected(other, box_with_array)
  1268. if box_with_array is pd.array and op is roperator.radd:
  1269. # We expect a NumpyExtensionArray, not ndarray[object] here
  1270. expected = pd.array(expected, dtype=object)
  1271. with tm.assert_produces_warning(PerformanceWarning):
  1272. res = op(dtarr, other)
  1273. tm.assert_equal(res, expected)
  1274. @pytest.mark.parametrize(
  1275. "op, offset, exp, exp_freq",
  1276. [
  1277. (
  1278. "__add__",
  1279. DateOffset(months=3, days=10),
  1280. [
  1281. Timestamp("2014-04-11"),
  1282. Timestamp("2015-04-11"),
  1283. Timestamp("2016-04-11"),
  1284. Timestamp("2017-04-11"),
  1285. ],
  1286. None,
  1287. ),
  1288. (
  1289. "__add__",
  1290. DateOffset(months=3),
  1291. [
  1292. Timestamp("2014-04-01"),
  1293. Timestamp("2015-04-01"),
  1294. Timestamp("2016-04-01"),
  1295. Timestamp("2017-04-01"),
  1296. ],
  1297. "YS-APR",
  1298. ),
  1299. (
  1300. "__sub__",
  1301. DateOffset(months=3, days=10),
  1302. [
  1303. Timestamp("2013-09-21"),
  1304. Timestamp("2014-09-21"),
  1305. Timestamp("2015-09-21"),
  1306. Timestamp("2016-09-21"),
  1307. ],
  1308. None,
  1309. ),
  1310. (
  1311. "__sub__",
  1312. DateOffset(months=3),
  1313. [
  1314. Timestamp("2013-10-01"),
  1315. Timestamp("2014-10-01"),
  1316. Timestamp("2015-10-01"),
  1317. Timestamp("2016-10-01"),
  1318. ],
  1319. "YS-OCT",
  1320. ),
  1321. ],
  1322. )
  1323. def test_dti_add_sub_nonzero_mth_offset(
  1324. self, op, offset, exp, exp_freq, tz_aware_fixture, box_with_array
  1325. ):
  1326. # GH 26258
  1327. tz = tz_aware_fixture
  1328. date = date_range(start="01 Jan 2014", end="01 Jan 2017", freq="YS", tz=tz)
  1329. date = tm.box_expected(date, box_with_array, False)
  1330. mth = getattr(date, op)
  1331. result = mth(offset)
  1332. expected = DatetimeIndex(exp, tz=tz).as_unit("ns")
  1333. expected = tm.box_expected(expected, box_with_array, False)
  1334. tm.assert_equal(result, expected)
  1335. def test_dt64arr_series_add_DateOffset_with_milli(self):
  1336. # GH 57529
  1337. dti = DatetimeIndex(
  1338. [
  1339. "2000-01-01 00:00:00.012345678",
  1340. "2000-01-31 00:00:00.012345678",
  1341. "2000-02-29 00:00:00.012345678",
  1342. ],
  1343. dtype="datetime64[ns]",
  1344. )
  1345. result = dti + DateOffset(milliseconds=4)
  1346. expected = DatetimeIndex(
  1347. [
  1348. "2000-01-01 00:00:00.016345678",
  1349. "2000-01-31 00:00:00.016345678",
  1350. "2000-02-29 00:00:00.016345678",
  1351. ],
  1352. dtype="datetime64[ns]",
  1353. )
  1354. tm.assert_index_equal(result, expected)
  1355. result = dti + DateOffset(days=1, milliseconds=4)
  1356. expected = DatetimeIndex(
  1357. [
  1358. "2000-01-02 00:00:00.016345678",
  1359. "2000-02-01 00:00:00.016345678",
  1360. "2000-03-01 00:00:00.016345678",
  1361. ],
  1362. dtype="datetime64[ns]",
  1363. )
  1364. tm.assert_index_equal(result, expected)
  1365. class TestDatetime64OverflowHandling:
  1366. # TODO: box + de-duplicate
  1367. def test_dt64_overflow_masking(self, box_with_array):
  1368. # GH#25317
  1369. left = Series([Timestamp("1969-12-31")], dtype="M8[ns]")
  1370. right = Series([NaT])
  1371. left = tm.box_expected(left, box_with_array)
  1372. right = tm.box_expected(right, box_with_array)
  1373. expected = TimedeltaIndex([NaT], dtype="m8[ns]")
  1374. expected = tm.box_expected(expected, box_with_array)
  1375. result = left - right
  1376. tm.assert_equal(result, expected)
  1377. def test_dt64_series_arith_overflow(self):
  1378. # GH#12534, fixed by GH#19024
  1379. dt = Timestamp("1700-01-31")
  1380. td = Timedelta("20000 Days")
  1381. dti = date_range("1949-09-30", freq="100YE", periods=4)
  1382. ser = Series(dti)
  1383. msg = "Overflow in int64 addition"
  1384. with pytest.raises(OverflowError, match=msg):
  1385. ser - dt
  1386. with pytest.raises(OverflowError, match=msg):
  1387. dt - ser
  1388. with pytest.raises(OverflowError, match=msg):
  1389. ser + td
  1390. with pytest.raises(OverflowError, match=msg):
  1391. td + ser
  1392. ser.iloc[-1] = NaT
  1393. expected = Series(
  1394. ["2004-10-03", "2104-10-04", "2204-10-04", "NaT"], dtype="datetime64[ns]"
  1395. )
  1396. res = ser + td
  1397. tm.assert_series_equal(res, expected)
  1398. res = td + ser
  1399. tm.assert_series_equal(res, expected)
  1400. ser.iloc[1:] = NaT
  1401. expected = Series(["91279 Days", "NaT", "NaT", "NaT"], dtype="timedelta64[ns]")
  1402. res = ser - dt
  1403. tm.assert_series_equal(res, expected)
  1404. res = dt - ser
  1405. tm.assert_series_equal(res, -expected)
  1406. def test_datetimeindex_sub_timestamp_overflow(self):
  1407. dtimax = pd.to_datetime(["2021-12-28 17:19", Timestamp.max]).as_unit("ns")
  1408. dtimin = pd.to_datetime(["2021-12-28 17:19", Timestamp.min]).as_unit("ns")
  1409. tsneg = Timestamp("1950-01-01").as_unit("ns")
  1410. ts_neg_variants = [
  1411. tsneg,
  1412. tsneg.to_pydatetime(),
  1413. tsneg.to_datetime64().astype("datetime64[ns]"),
  1414. tsneg.to_datetime64().astype("datetime64[D]"),
  1415. ]
  1416. tspos = Timestamp("1980-01-01").as_unit("ns")
  1417. ts_pos_variants = [
  1418. tspos,
  1419. tspos.to_pydatetime(),
  1420. tspos.to_datetime64().astype("datetime64[ns]"),
  1421. tspos.to_datetime64().astype("datetime64[D]"),
  1422. ]
  1423. msg = "Overflow in int64 addition"
  1424. for variant in ts_neg_variants:
  1425. with pytest.raises(OverflowError, match=msg):
  1426. dtimax - variant
  1427. expected = Timestamp.max._value - tspos._value
  1428. for variant in ts_pos_variants:
  1429. res = dtimax - variant
  1430. assert res[1]._value == expected
  1431. expected = Timestamp.min._value - tsneg._value
  1432. for variant in ts_neg_variants:
  1433. res = dtimin - variant
  1434. assert res[1]._value == expected
  1435. for variant in ts_pos_variants:
  1436. with pytest.raises(OverflowError, match=msg):
  1437. dtimin - variant
  1438. def test_datetimeindex_sub_datetimeindex_overflow(self):
  1439. # GH#22492, GH#22508
  1440. dtimax = pd.to_datetime(["2021-12-28 17:19", Timestamp.max]).as_unit("ns")
  1441. dtimin = pd.to_datetime(["2021-12-28 17:19", Timestamp.min]).as_unit("ns")
  1442. ts_neg = pd.to_datetime(["1950-01-01", "1950-01-01"]).as_unit("ns")
  1443. ts_pos = pd.to_datetime(["1980-01-01", "1980-01-01"]).as_unit("ns")
  1444. # General tests
  1445. expected = Timestamp.max._value - ts_pos[1]._value
  1446. result = dtimax - ts_pos
  1447. assert result[1]._value == expected
  1448. expected = Timestamp.min._value - ts_neg[1]._value
  1449. result = dtimin - ts_neg
  1450. assert result[1]._value == expected
  1451. msg = "Overflow in int64 addition"
  1452. with pytest.raises(OverflowError, match=msg):
  1453. dtimax - ts_neg
  1454. with pytest.raises(OverflowError, match=msg):
  1455. dtimin - ts_pos
  1456. # Edge cases
  1457. tmin = pd.to_datetime([Timestamp.min])
  1458. t1 = tmin + Timedelta.max + Timedelta("1us")
  1459. with pytest.raises(OverflowError, match=msg):
  1460. t1 - tmin
  1461. tmax = pd.to_datetime([Timestamp.max])
  1462. t2 = tmax + Timedelta.min - Timedelta("1us")
  1463. with pytest.raises(OverflowError, match=msg):
  1464. tmax - t2
  1465. class TestTimestampSeriesArithmetic:
  1466. def test_empty_series_add_sub(self, box_with_array):
  1467. # GH#13844
  1468. a = Series(dtype="M8[ns]")
  1469. b = Series(dtype="m8[ns]")
  1470. a = box_with_array(a)
  1471. b = box_with_array(b)
  1472. tm.assert_equal(a, a + b)
  1473. tm.assert_equal(a, a - b)
  1474. tm.assert_equal(a, b + a)
  1475. msg = "cannot subtract"
  1476. with pytest.raises(TypeError, match=msg):
  1477. b - a
  1478. def test_operators_datetimelike(self):
  1479. # ## timedelta64 ###
  1480. td1 = Series([timedelta(minutes=5, seconds=3)] * 3)
  1481. td1.iloc[2] = np.nan
  1482. # ## datetime64 ###
  1483. dt1 = Series(
  1484. [
  1485. Timestamp("20111230"),
  1486. Timestamp("20120101"),
  1487. Timestamp("20120103"),
  1488. ]
  1489. )
  1490. dt1.iloc[2] = np.nan
  1491. dt2 = Series(
  1492. [
  1493. Timestamp("20111231"),
  1494. Timestamp("20120102"),
  1495. Timestamp("20120104"),
  1496. ]
  1497. )
  1498. dt1 - dt2
  1499. dt2 - dt1
  1500. # datetime64 with timetimedelta
  1501. dt1 + td1
  1502. td1 + dt1
  1503. dt1 - td1
  1504. # timetimedelta with datetime64
  1505. td1 + dt1
  1506. dt1 + td1
  1507. def test_dt64ser_sub_datetime_dtype(self, unit):
  1508. ts = Timestamp(datetime(1993, 1, 7, 13, 30, 00))
  1509. dt = datetime(1993, 6, 22, 13, 30)
  1510. ser = Series([ts], dtype=f"M8[{unit}]")
  1511. result = ser - dt
  1512. # the expected unit is the max of `unit` and the unit imputed to `dt`,
  1513. # which is "us"
  1514. exp_unit = tm.get_finest_unit(unit, "us")
  1515. assert result.dtype == f"timedelta64[{exp_unit}]"
  1516. # -------------------------------------------------------------
  1517. # TODO: This next block of tests came from tests.series.test_operators,
  1518. # needs to be de-duplicated and parametrized over `box` classes
  1519. @pytest.mark.parametrize(
  1520. "left, right, op_fail",
  1521. [
  1522. [
  1523. [Timestamp("20111230"), Timestamp("20120101"), NaT],
  1524. [Timestamp("20111231"), Timestamp("20120102"), Timestamp("20120104")],
  1525. ["__sub__", "__rsub__"],
  1526. ],
  1527. [
  1528. [Timestamp("20111230"), Timestamp("20120101"), NaT],
  1529. [timedelta(minutes=5, seconds=3), timedelta(minutes=5, seconds=3), NaT],
  1530. ["__add__", "__radd__", "__sub__"],
  1531. ],
  1532. [
  1533. [
  1534. Timestamp("20111230", tz="US/Eastern"),
  1535. Timestamp("20111230", tz="US/Eastern"),
  1536. NaT,
  1537. ],
  1538. [timedelta(minutes=5, seconds=3), NaT, timedelta(minutes=5, seconds=3)],
  1539. ["__add__", "__radd__", "__sub__"],
  1540. ],
  1541. ],
  1542. )
  1543. def test_operators_datetimelike_invalid(
  1544. self, left, right, op_fail, all_arithmetic_operators
  1545. ):
  1546. # these are all TypeError ops
  1547. op_str = all_arithmetic_operators
  1548. arg1 = Series(left)
  1549. arg2 = Series(right)
  1550. # check that we are getting a TypeError
  1551. # with 'operate' (from core/ops.py) for the ops that are not
  1552. # defined
  1553. op = getattr(arg1, op_str, None)
  1554. # Previously, _validate_for_numeric_binop in core/indexes/base.py
  1555. # did this for us.
  1556. if op_str not in op_fail:
  1557. with pytest.raises(
  1558. TypeError, match="operate|[cC]annot|unsupported operand"
  1559. ):
  1560. op(arg2)
  1561. else:
  1562. # Smoke test
  1563. op(arg2)
  1564. def test_sub_single_tz(self, unit):
  1565. # GH#12290
  1566. s1 = Series([Timestamp("2016-02-10", tz="America/Sao_Paulo")]).dt.as_unit(unit)
  1567. s2 = Series([Timestamp("2016-02-08", tz="America/Sao_Paulo")]).dt.as_unit(unit)
  1568. result = s1 - s2
  1569. expected = Series([Timedelta("2days")]).dt.as_unit(unit)
  1570. tm.assert_series_equal(result, expected)
  1571. result = s2 - s1
  1572. expected = Series([Timedelta("-2days")]).dt.as_unit(unit)
  1573. tm.assert_series_equal(result, expected)
  1574. def test_dt64tz_series_sub_dtitz(self):
  1575. # GH#19071 subtracting tzaware DatetimeIndex from tzaware Series
  1576. # (with same tz) raises, fixed by #19024
  1577. dti = date_range("1999-09-30", periods=10, tz="US/Pacific")
  1578. ser = Series(dti)
  1579. expected = Series(TimedeltaIndex(["0days"] * 10))
  1580. res = dti - ser
  1581. tm.assert_series_equal(res, expected)
  1582. res = ser - dti
  1583. tm.assert_series_equal(res, expected)
  1584. def test_sub_datetime_compat(self, unit):
  1585. # see GH#14088
  1586. ser = Series([datetime(2016, 8, 23, 12, tzinfo=pytz.utc), NaT]).dt.as_unit(unit)
  1587. dt = datetime(2016, 8, 22, 12, tzinfo=pytz.utc)
  1588. # The datetime object has "us" so we upcast lower units
  1589. exp_unit = tm.get_finest_unit(unit, "us")
  1590. exp = Series([Timedelta("1 days"), NaT]).dt.as_unit(exp_unit)
  1591. result = ser - dt
  1592. tm.assert_series_equal(result, exp)
  1593. result2 = ser - Timestamp(dt)
  1594. tm.assert_series_equal(result2, exp)
  1595. def test_dt64_series_add_mixed_tick_DateOffset(self):
  1596. # GH#4532
  1597. # operate with pd.offsets
  1598. s = Series([Timestamp("20130101 9:01"), Timestamp("20130101 9:02")])
  1599. result = s + pd.offsets.Milli(5)
  1600. result2 = pd.offsets.Milli(5) + s
  1601. expected = Series(
  1602. [Timestamp("20130101 9:01:00.005"), Timestamp("20130101 9:02:00.005")]
  1603. )
  1604. tm.assert_series_equal(result, expected)
  1605. tm.assert_series_equal(result2, expected)
  1606. result = s + pd.offsets.Minute(5) + pd.offsets.Milli(5)
  1607. expected = Series(
  1608. [Timestamp("20130101 9:06:00.005"), Timestamp("20130101 9:07:00.005")]
  1609. )
  1610. tm.assert_series_equal(result, expected)
  1611. def test_datetime64_ops_nat(self, unit):
  1612. # GH#11349
  1613. datetime_series = Series([NaT, Timestamp("19900315")]).dt.as_unit(unit)
  1614. nat_series_dtype_timestamp = Series([NaT, NaT], dtype=f"datetime64[{unit}]")
  1615. single_nat_dtype_datetime = Series([NaT], dtype=f"datetime64[{unit}]")
  1616. # subtraction
  1617. tm.assert_series_equal(-NaT + datetime_series, nat_series_dtype_timestamp)
  1618. msg = "bad operand type for unary -: 'DatetimeArray'"
  1619. with pytest.raises(TypeError, match=msg):
  1620. -single_nat_dtype_datetime + datetime_series
  1621. tm.assert_series_equal(
  1622. -NaT + nat_series_dtype_timestamp, nat_series_dtype_timestamp
  1623. )
  1624. with pytest.raises(TypeError, match=msg):
  1625. -single_nat_dtype_datetime + nat_series_dtype_timestamp
  1626. # addition
  1627. tm.assert_series_equal(
  1628. nat_series_dtype_timestamp + NaT, nat_series_dtype_timestamp
  1629. )
  1630. tm.assert_series_equal(
  1631. NaT + nat_series_dtype_timestamp, nat_series_dtype_timestamp
  1632. )
  1633. tm.assert_series_equal(
  1634. nat_series_dtype_timestamp + NaT, nat_series_dtype_timestamp
  1635. )
  1636. tm.assert_series_equal(
  1637. NaT + nat_series_dtype_timestamp, nat_series_dtype_timestamp
  1638. )
  1639. # -------------------------------------------------------------
  1640. # Timezone-Centric Tests
  1641. def test_operators_datetimelike_with_timezones(self):
  1642. tz = "US/Eastern"
  1643. dt1 = Series(date_range("2000-01-01 09:00:00", periods=5, tz=tz), name="foo")
  1644. dt2 = dt1.copy()
  1645. dt2.iloc[2] = np.nan
  1646. td1 = Series(pd.timedelta_range("1 days 1 min", periods=5, freq="h"))
  1647. td2 = td1.copy()
  1648. td2.iloc[1] = np.nan
  1649. assert td2._values.freq is None
  1650. result = dt1 + td1[0]
  1651. exp = (dt1.dt.tz_localize(None) + td1[0]).dt.tz_localize(tz)
  1652. tm.assert_series_equal(result, exp)
  1653. result = dt2 + td2[0]
  1654. exp = (dt2.dt.tz_localize(None) + td2[0]).dt.tz_localize(tz)
  1655. tm.assert_series_equal(result, exp)
  1656. # odd numpy behavior with scalar timedeltas
  1657. result = td1[0] + dt1
  1658. exp = (dt1.dt.tz_localize(None) + td1[0]).dt.tz_localize(tz)
  1659. tm.assert_series_equal(result, exp)
  1660. result = td2[0] + dt2
  1661. exp = (dt2.dt.tz_localize(None) + td2[0]).dt.tz_localize(tz)
  1662. tm.assert_series_equal(result, exp)
  1663. result = dt1 - td1[0]
  1664. exp = (dt1.dt.tz_localize(None) - td1[0]).dt.tz_localize(tz)
  1665. tm.assert_series_equal(result, exp)
  1666. msg = "(bad|unsupported) operand type for unary"
  1667. with pytest.raises(TypeError, match=msg):
  1668. td1[0] - dt1
  1669. result = dt2 - td2[0]
  1670. exp = (dt2.dt.tz_localize(None) - td2[0]).dt.tz_localize(tz)
  1671. tm.assert_series_equal(result, exp)
  1672. with pytest.raises(TypeError, match=msg):
  1673. td2[0] - dt2
  1674. result = dt1 + td1
  1675. exp = (dt1.dt.tz_localize(None) + td1).dt.tz_localize(tz)
  1676. tm.assert_series_equal(result, exp)
  1677. result = dt2 + td2
  1678. exp = (dt2.dt.tz_localize(None) + td2).dt.tz_localize(tz)
  1679. tm.assert_series_equal(result, exp)
  1680. result = dt1 - td1
  1681. exp = (dt1.dt.tz_localize(None) - td1).dt.tz_localize(tz)
  1682. tm.assert_series_equal(result, exp)
  1683. result = dt2 - td2
  1684. exp = (dt2.dt.tz_localize(None) - td2).dt.tz_localize(tz)
  1685. tm.assert_series_equal(result, exp)
  1686. msg = "cannot (add|subtract)"
  1687. with pytest.raises(TypeError, match=msg):
  1688. td1 - dt1
  1689. with pytest.raises(TypeError, match=msg):
  1690. td2 - dt2
  1691. class TestDatetimeIndexArithmetic:
  1692. # -------------------------------------------------------------
  1693. # Binary operations DatetimeIndex and TimedeltaIndex/array
  1694. def test_dti_add_tdi(self, tz_naive_fixture):
  1695. # GH#17558
  1696. tz = tz_naive_fixture
  1697. dti = DatetimeIndex([Timestamp("2017-01-01", tz=tz)] * 10)
  1698. tdi = pd.timedelta_range("0 days", periods=10)
  1699. expected = date_range("2017-01-01", periods=10, tz=tz)
  1700. expected = expected._with_freq(None)
  1701. # add with TimedeltaIndex
  1702. result = dti + tdi
  1703. tm.assert_index_equal(result, expected)
  1704. result = tdi + dti
  1705. tm.assert_index_equal(result, expected)
  1706. # add with timedelta64 array
  1707. result = dti + tdi.values
  1708. tm.assert_index_equal(result, expected)
  1709. result = tdi.values + dti
  1710. tm.assert_index_equal(result, expected)
  1711. def test_dti_iadd_tdi(self, tz_naive_fixture):
  1712. # GH#17558
  1713. tz = tz_naive_fixture
  1714. dti = DatetimeIndex([Timestamp("2017-01-01", tz=tz)] * 10)
  1715. tdi = pd.timedelta_range("0 days", periods=10)
  1716. expected = date_range("2017-01-01", periods=10, tz=tz)
  1717. expected = expected._with_freq(None)
  1718. # iadd with TimedeltaIndex
  1719. result = DatetimeIndex([Timestamp("2017-01-01", tz=tz)] * 10)
  1720. result += tdi
  1721. tm.assert_index_equal(result, expected)
  1722. result = pd.timedelta_range("0 days", periods=10)
  1723. result += dti
  1724. tm.assert_index_equal(result, expected)
  1725. # iadd with timedelta64 array
  1726. result = DatetimeIndex([Timestamp("2017-01-01", tz=tz)] * 10)
  1727. result += tdi.values
  1728. tm.assert_index_equal(result, expected)
  1729. result = pd.timedelta_range("0 days", periods=10)
  1730. result += dti
  1731. tm.assert_index_equal(result, expected)
  1732. def test_dti_sub_tdi(self, tz_naive_fixture):
  1733. # GH#17558
  1734. tz = tz_naive_fixture
  1735. dti = DatetimeIndex([Timestamp("2017-01-01", tz=tz)] * 10)
  1736. tdi = pd.timedelta_range("0 days", periods=10)
  1737. expected = date_range("2017-01-01", periods=10, tz=tz, freq="-1D")
  1738. expected = expected._with_freq(None)
  1739. # sub with TimedeltaIndex
  1740. result = dti - tdi
  1741. tm.assert_index_equal(result, expected)
  1742. msg = "cannot subtract .*TimedeltaArray"
  1743. with pytest.raises(TypeError, match=msg):
  1744. tdi - dti
  1745. # sub with timedelta64 array
  1746. result = dti - tdi.values
  1747. tm.assert_index_equal(result, expected)
  1748. msg = "cannot subtract a datelike from a TimedeltaArray"
  1749. with pytest.raises(TypeError, match=msg):
  1750. tdi.values - dti
  1751. def test_dti_isub_tdi(self, tz_naive_fixture, unit):
  1752. # GH#17558
  1753. tz = tz_naive_fixture
  1754. dti = DatetimeIndex([Timestamp("2017-01-01", tz=tz)] * 10).as_unit(unit)
  1755. tdi = pd.timedelta_range("0 days", periods=10, unit=unit)
  1756. expected = date_range("2017-01-01", periods=10, tz=tz, freq="-1D", unit=unit)
  1757. expected = expected._with_freq(None)
  1758. # isub with TimedeltaIndex
  1759. result = DatetimeIndex([Timestamp("2017-01-01", tz=tz)] * 10).as_unit(unit)
  1760. result -= tdi
  1761. tm.assert_index_equal(result, expected)
  1762. # DTA.__isub__ GH#43904
  1763. dta = dti._data.copy()
  1764. dta -= tdi
  1765. tm.assert_datetime_array_equal(dta, expected._data)
  1766. out = dti._data.copy()
  1767. np.subtract(out, tdi, out=out)
  1768. tm.assert_datetime_array_equal(out, expected._data)
  1769. msg = "cannot subtract a datelike from a TimedeltaArray"
  1770. with pytest.raises(TypeError, match=msg):
  1771. tdi -= dti
  1772. # isub with timedelta64 array
  1773. result = DatetimeIndex([Timestamp("2017-01-01", tz=tz)] * 10).as_unit(unit)
  1774. result -= tdi.values
  1775. tm.assert_index_equal(result, expected)
  1776. with pytest.raises(TypeError, match=msg):
  1777. tdi.values -= dti
  1778. with pytest.raises(TypeError, match=msg):
  1779. tdi._values -= dti
  1780. # -------------------------------------------------------------
  1781. # Binary Operations DatetimeIndex and datetime-like
  1782. # TODO: A couple other tests belong in this section. Move them in
  1783. # A PR where there isn't already a giant diff.
  1784. # -------------------------------------------------------------
  1785. def test_dta_add_sub_index(self, tz_naive_fixture):
  1786. # Check that DatetimeArray defers to Index classes
  1787. dti = date_range("20130101", periods=3, tz=tz_naive_fixture)
  1788. dta = dti.array
  1789. result = dta - dti
  1790. expected = dti - dti
  1791. tm.assert_index_equal(result, expected)
  1792. tdi = result
  1793. result = dta + tdi
  1794. expected = dti + tdi
  1795. tm.assert_index_equal(result, expected)
  1796. result = dta - tdi
  1797. expected = dti - tdi
  1798. tm.assert_index_equal(result, expected)
  1799. def test_sub_dti_dti(self, unit):
  1800. # previously performed setop (deprecated in 0.16.0), now changed to
  1801. # return subtraction -> TimeDeltaIndex (GH ...)
  1802. dti = date_range("20130101", periods=3, unit=unit)
  1803. dti_tz = date_range("20130101", periods=3, unit=unit).tz_localize("US/Eastern")
  1804. expected = TimedeltaIndex([0, 0, 0]).as_unit(unit)
  1805. result = dti - dti
  1806. tm.assert_index_equal(result, expected)
  1807. result = dti_tz - dti_tz
  1808. tm.assert_index_equal(result, expected)
  1809. msg = "Cannot subtract tz-naive and tz-aware datetime-like objects"
  1810. with pytest.raises(TypeError, match=msg):
  1811. dti_tz - dti
  1812. with pytest.raises(TypeError, match=msg):
  1813. dti - dti_tz
  1814. # isub
  1815. dti -= dti
  1816. tm.assert_index_equal(dti, expected)
  1817. # different length raises ValueError
  1818. dti1 = date_range("20130101", periods=3, unit=unit)
  1819. dti2 = date_range("20130101", periods=4, unit=unit)
  1820. msg = "cannot add indices of unequal length"
  1821. with pytest.raises(ValueError, match=msg):
  1822. dti1 - dti2
  1823. # NaN propagation
  1824. dti1 = DatetimeIndex(["2012-01-01", np.nan, "2012-01-03"]).as_unit(unit)
  1825. dti2 = DatetimeIndex(["2012-01-02", "2012-01-03", np.nan]).as_unit(unit)
  1826. expected = TimedeltaIndex(["1 days", np.nan, np.nan]).as_unit(unit)
  1827. result = dti2 - dti1
  1828. tm.assert_index_equal(result, expected)
  1829. # -------------------------------------------------------------------
  1830. # TODO: Most of this block is moved from series or frame tests, needs
  1831. # cleanup, box-parametrization, and de-duplication
  1832. @pytest.mark.parametrize("op", [operator.add, operator.sub])
  1833. def test_timedelta64_equal_timedelta_supported_ops(self, op, box_with_array):
  1834. ser = Series(
  1835. [
  1836. Timestamp("20130301"),
  1837. Timestamp("20130228 23:00:00"),
  1838. Timestamp("20130228 22:00:00"),
  1839. Timestamp("20130228 21:00:00"),
  1840. ]
  1841. )
  1842. obj = box_with_array(ser)
  1843. intervals = ["D", "h", "m", "s", "us"]
  1844. def timedelta64(*args):
  1845. # see casting notes in NumPy gh-12927
  1846. return np.sum(list(starmap(np.timedelta64, zip(args, intervals))))
  1847. for d, h, m, s, us in product(*([range(2)] * 5)):
  1848. nptd = timedelta64(d, h, m, s, us)
  1849. pytd = timedelta(days=d, hours=h, minutes=m, seconds=s, microseconds=us)
  1850. lhs = op(obj, nptd)
  1851. rhs = op(obj, pytd)
  1852. tm.assert_equal(lhs, rhs)
  1853. def test_ops_nat_mixed_datetime64_timedelta64(self):
  1854. # GH#11349
  1855. timedelta_series = Series([NaT, Timedelta("1s")])
  1856. datetime_series = Series([NaT, Timestamp("19900315")])
  1857. nat_series_dtype_timedelta = Series([NaT, NaT], dtype="timedelta64[ns]")
  1858. nat_series_dtype_timestamp = Series([NaT, NaT], dtype="datetime64[ns]")
  1859. single_nat_dtype_datetime = Series([NaT], dtype="datetime64[ns]")
  1860. single_nat_dtype_timedelta = Series([NaT], dtype="timedelta64[ns]")
  1861. # subtraction
  1862. tm.assert_series_equal(
  1863. datetime_series - single_nat_dtype_datetime, nat_series_dtype_timedelta
  1864. )
  1865. tm.assert_series_equal(
  1866. datetime_series - single_nat_dtype_timedelta, nat_series_dtype_timestamp
  1867. )
  1868. tm.assert_series_equal(
  1869. -single_nat_dtype_timedelta + datetime_series, nat_series_dtype_timestamp
  1870. )
  1871. # without a Series wrapping the NaT, it is ambiguous
  1872. # whether it is a datetime64 or timedelta64
  1873. # defaults to interpreting it as timedelta64
  1874. tm.assert_series_equal(
  1875. nat_series_dtype_timestamp - single_nat_dtype_datetime,
  1876. nat_series_dtype_timedelta,
  1877. )
  1878. tm.assert_series_equal(
  1879. nat_series_dtype_timestamp - single_nat_dtype_timedelta,
  1880. nat_series_dtype_timestamp,
  1881. )
  1882. tm.assert_series_equal(
  1883. -single_nat_dtype_timedelta + nat_series_dtype_timestamp,
  1884. nat_series_dtype_timestamp,
  1885. )
  1886. msg = "cannot subtract a datelike"
  1887. with pytest.raises(TypeError, match=msg):
  1888. timedelta_series - single_nat_dtype_datetime
  1889. # addition
  1890. tm.assert_series_equal(
  1891. nat_series_dtype_timestamp + single_nat_dtype_timedelta,
  1892. nat_series_dtype_timestamp,
  1893. )
  1894. tm.assert_series_equal(
  1895. single_nat_dtype_timedelta + nat_series_dtype_timestamp,
  1896. nat_series_dtype_timestamp,
  1897. )
  1898. tm.assert_series_equal(
  1899. nat_series_dtype_timestamp + single_nat_dtype_timedelta,
  1900. nat_series_dtype_timestamp,
  1901. )
  1902. tm.assert_series_equal(
  1903. single_nat_dtype_timedelta + nat_series_dtype_timestamp,
  1904. nat_series_dtype_timestamp,
  1905. )
  1906. tm.assert_series_equal(
  1907. nat_series_dtype_timedelta + single_nat_dtype_datetime,
  1908. nat_series_dtype_timestamp,
  1909. )
  1910. tm.assert_series_equal(
  1911. single_nat_dtype_datetime + nat_series_dtype_timedelta,
  1912. nat_series_dtype_timestamp,
  1913. )
  1914. def test_ufunc_coercions(self, unit):
  1915. idx = date_range("2011-01-01", periods=3, freq="2D", name="x", unit=unit)
  1916. delta = np.timedelta64(1, "D")
  1917. exp = date_range("2011-01-02", periods=3, freq="2D", name="x", unit=unit)
  1918. for result in [idx + delta, np.add(idx, delta)]:
  1919. assert isinstance(result, DatetimeIndex)
  1920. tm.assert_index_equal(result, exp)
  1921. assert result.freq == "2D"
  1922. exp = date_range("2010-12-31", periods=3, freq="2D", name="x", unit=unit)
  1923. for result in [idx - delta, np.subtract(idx, delta)]:
  1924. assert isinstance(result, DatetimeIndex)
  1925. tm.assert_index_equal(result, exp)
  1926. assert result.freq == "2D"
  1927. # When adding/subtracting an ndarray (which has no .freq), the result
  1928. # does not infer freq
  1929. idx = idx._with_freq(None)
  1930. delta = np.array(
  1931. [np.timedelta64(1, "D"), np.timedelta64(2, "D"), np.timedelta64(3, "D")]
  1932. )
  1933. exp = DatetimeIndex(
  1934. ["2011-01-02", "2011-01-05", "2011-01-08"], name="x"
  1935. ).as_unit(unit)
  1936. for result in [idx + delta, np.add(idx, delta)]:
  1937. tm.assert_index_equal(result, exp)
  1938. assert result.freq == exp.freq
  1939. exp = DatetimeIndex(
  1940. ["2010-12-31", "2011-01-01", "2011-01-02"], name="x"
  1941. ).as_unit(unit)
  1942. for result in [idx - delta, np.subtract(idx, delta)]:
  1943. assert isinstance(result, DatetimeIndex)
  1944. tm.assert_index_equal(result, exp)
  1945. assert result.freq == exp.freq
  1946. def test_dti_add_series(self, tz_naive_fixture, names):
  1947. # GH#13905
  1948. tz = tz_naive_fixture
  1949. index = DatetimeIndex(
  1950. ["2016-06-28 05:30", "2016-06-28 05:31"], tz=tz, name=names[0]
  1951. ).as_unit("ns")
  1952. ser = Series([Timedelta(seconds=5)] * 2, index=index, name=names[1])
  1953. expected = Series(index + Timedelta(seconds=5), index=index, name=names[2])
  1954. # passing name arg isn't enough when names[2] is None
  1955. expected.name = names[2]
  1956. assert expected.dtype == index.dtype
  1957. result = ser + index
  1958. tm.assert_series_equal(result, expected)
  1959. result2 = index + ser
  1960. tm.assert_series_equal(result2, expected)
  1961. expected = index + Timedelta(seconds=5)
  1962. result3 = ser.values + index
  1963. tm.assert_index_equal(result3, expected)
  1964. result4 = index + ser.values
  1965. tm.assert_index_equal(result4, expected)
  1966. @pytest.mark.parametrize("op", [operator.add, roperator.radd, operator.sub])
  1967. def test_dti_addsub_offset_arraylike(
  1968. self, tz_naive_fixture, names, op, index_or_series
  1969. ):
  1970. # GH#18849, GH#19744
  1971. other_box = index_or_series
  1972. tz = tz_naive_fixture
  1973. dti = date_range("2017-01-01", periods=2, tz=tz, name=names[0])
  1974. other = other_box([pd.offsets.MonthEnd(), pd.offsets.Day(n=2)], name=names[1])
  1975. xbox = get_upcast_box(dti, other)
  1976. with tm.assert_produces_warning(PerformanceWarning):
  1977. res = op(dti, other)
  1978. expected = DatetimeIndex(
  1979. [op(dti[n], other[n]) for n in range(len(dti))], name=names[2], freq="infer"
  1980. )
  1981. expected = tm.box_expected(expected, xbox).astype(object)
  1982. tm.assert_equal(res, expected)
  1983. @pytest.mark.parametrize("other_box", [pd.Index, np.array])
  1984. def test_dti_addsub_object_arraylike(
  1985. self, tz_naive_fixture, box_with_array, other_box
  1986. ):
  1987. tz = tz_naive_fixture
  1988. dti = date_range("2017-01-01", periods=2, tz=tz)
  1989. dtarr = tm.box_expected(dti, box_with_array)
  1990. other = other_box([pd.offsets.MonthEnd(), Timedelta(days=4)])
  1991. xbox = get_upcast_box(dtarr, other)
  1992. expected = DatetimeIndex(["2017-01-31", "2017-01-06"], tz=tz_naive_fixture)
  1993. expected = tm.box_expected(expected, xbox).astype(object)
  1994. with tm.assert_produces_warning(PerformanceWarning):
  1995. result = dtarr + other
  1996. tm.assert_equal(result, expected)
  1997. expected = DatetimeIndex(["2016-12-31", "2016-12-29"], tz=tz_naive_fixture)
  1998. expected = tm.box_expected(expected, xbox).astype(object)
  1999. with tm.assert_produces_warning(PerformanceWarning):
  2000. result = dtarr - other
  2001. tm.assert_equal(result, expected)
  2002. @pytest.mark.parametrize("years", [-1, 0, 1])
  2003. @pytest.mark.parametrize("months", [-2, 0, 2])
  2004. @pytest.mark.parametrize("unit", ["s", "ms", "us", "ns"])
  2005. def test_shift_months(years, months, unit):
  2006. dti = DatetimeIndex(
  2007. [
  2008. Timestamp("2000-01-05 00:15:00"),
  2009. Timestamp("2000-01-31 00:23:00"),
  2010. Timestamp("2000-01-01"),
  2011. Timestamp("2000-02-29"),
  2012. Timestamp("2000-12-31"),
  2013. ]
  2014. ).as_unit(unit)
  2015. shifted = shift_months(dti.asi8, years * 12 + months, reso=dti._data._creso)
  2016. shifted_dt64 = shifted.view(f"M8[{dti.unit}]")
  2017. actual = DatetimeIndex(shifted_dt64)
  2018. raw = [x + pd.offsets.DateOffset(years=years, months=months) for x in dti]
  2019. expected = DatetimeIndex(raw).as_unit(dti.unit)
  2020. tm.assert_index_equal(actual, expected)
  2021. def test_dt64arr_addsub_object_dtype_2d():
  2022. # block-wise DataFrame operations will require operating on 2D
  2023. # DatetimeArray/TimedeltaArray, so check that specifically.
  2024. dti = date_range("1994-02-13", freq="2W", periods=4)
  2025. dta = dti._data.reshape((4, 1))
  2026. other = np.array([[pd.offsets.Day(n)] for n in range(4)])
  2027. assert other.shape == dta.shape
  2028. with tm.assert_produces_warning(PerformanceWarning):
  2029. result = dta + other
  2030. with tm.assert_produces_warning(PerformanceWarning):
  2031. expected = (dta[:, 0] + other[:, 0]).reshape(-1, 1)
  2032. tm.assert_numpy_array_equal(result, expected)
  2033. with tm.assert_produces_warning(PerformanceWarning):
  2034. # Case where we expect to get a TimedeltaArray back
  2035. result2 = dta - dta.astype(object)
  2036. assert result2.shape == (4, 1)
  2037. assert all(td._value == 0 for td in result2.ravel())
  2038. def test_non_nano_dt64_addsub_np_nat_scalars():
  2039. # GH 52295
  2040. ser = Series([1233242342344, 232432434324, 332434242344], dtype="datetime64[ms]")
  2041. result = ser - np.datetime64("nat", "ms")
  2042. expected = Series([NaT] * 3, dtype="timedelta64[ms]")
  2043. tm.assert_series_equal(result, expected)
  2044. result = ser + np.timedelta64("nat", "ms")
  2045. expected = Series([NaT] * 3, dtype="datetime64[ms]")
  2046. tm.assert_series_equal(result, expected)
  2047. def test_non_nano_dt64_addsub_np_nat_scalars_unitless():
  2048. # GH 52295
  2049. # TODO: Can we default to the ser unit?
  2050. ser = Series([1233242342344, 232432434324, 332434242344], dtype="datetime64[ms]")
  2051. result = ser - np.datetime64("nat")
  2052. expected = Series([NaT] * 3, dtype="timedelta64[ns]")
  2053. tm.assert_series_equal(result, expected)
  2054. result = ser + np.timedelta64("nat")
  2055. expected = Series([NaT] * 3, dtype="datetime64[ns]")
  2056. tm.assert_series_equal(result, expected)
  2057. def test_non_nano_dt64_addsub_np_nat_scalars_unsupported_unit():
  2058. # GH 52295
  2059. ser = Series([12332, 23243, 33243], dtype="datetime64[s]")
  2060. result = ser - np.datetime64("nat", "D")
  2061. expected = Series([NaT] * 3, dtype="timedelta64[s]")
  2062. tm.assert_series_equal(result, expected)
  2063. result = ser + np.timedelta64("nat", "D")
  2064. expected = Series([NaT] * 3, dtype="datetime64[s]")
  2065. tm.assert_series_equal(result, expected)