test_business_hour.py 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463
  1. """
  2. Tests for offsets.BusinessHour
  3. """
  4. from __future__ import annotations
  5. from datetime import (
  6. datetime,
  7. time as dt_time,
  8. )
  9. import pytest
  10. from pandas._libs.tslibs import (
  11. Timedelta,
  12. Timestamp,
  13. )
  14. from pandas._libs.tslibs.offsets import (
  15. BDay,
  16. BusinessHour,
  17. Nano,
  18. )
  19. from pandas import (
  20. DatetimeIndex,
  21. _testing as tm,
  22. date_range,
  23. )
  24. from pandas.tests.tseries.offsets.common import assert_offset_equal
  25. @pytest.fixture
  26. def dt():
  27. return datetime(2014, 7, 1, 10, 00)
  28. @pytest.fixture
  29. def _offset():
  30. return BusinessHour
  31. @pytest.fixture
  32. def offset1():
  33. return BusinessHour()
  34. @pytest.fixture
  35. def offset2():
  36. return BusinessHour(n=3)
  37. @pytest.fixture
  38. def offset3():
  39. return BusinessHour(n=-1)
  40. @pytest.fixture
  41. def offset4():
  42. return BusinessHour(n=-4)
  43. @pytest.fixture
  44. def offset5():
  45. return BusinessHour(start=dt_time(11, 0), end=dt_time(14, 30))
  46. @pytest.fixture
  47. def offset6():
  48. return BusinessHour(start="20:00", end="05:00")
  49. @pytest.fixture
  50. def offset7():
  51. return BusinessHour(n=-2, start=dt_time(21, 30), end=dt_time(6, 30))
  52. @pytest.fixture
  53. def offset8():
  54. return BusinessHour(start=["09:00", "13:00"], end=["12:00", "17:00"])
  55. @pytest.fixture
  56. def offset9():
  57. return BusinessHour(n=3, start=["09:00", "22:00"], end=["13:00", "03:00"])
  58. @pytest.fixture
  59. def offset10():
  60. return BusinessHour(n=-1, start=["23:00", "13:00"], end=["02:00", "17:00"])
  61. class TestBusinessHour:
  62. @pytest.mark.parametrize(
  63. "start,end,match",
  64. [
  65. (
  66. dt_time(11, 0, 5),
  67. "17:00",
  68. "time data must be specified only with hour and minute",
  69. ),
  70. ("AAA", "17:00", "time data must match '%H:%M' format"),
  71. ("14:00:05", "17:00", "time data must match '%H:%M' format"),
  72. ([], "17:00", "Must include at least 1 start time"),
  73. ("09:00", [], "Must include at least 1 end time"),
  74. (
  75. ["09:00", "11:00"],
  76. "17:00",
  77. "number of starting time and ending time must be the same",
  78. ),
  79. (
  80. ["09:00", "11:00"],
  81. ["10:00"],
  82. "number of starting time and ending time must be the same",
  83. ),
  84. (
  85. ["09:00", "11:00"],
  86. ["12:00", "20:00"],
  87. r"invalid starting and ending time\(s\): opening hours should not "
  88. "touch or overlap with one another",
  89. ),
  90. (
  91. ["12:00", "20:00"],
  92. ["09:00", "11:00"],
  93. r"invalid starting and ending time\(s\): opening hours should not "
  94. "touch or overlap with one another",
  95. ),
  96. ],
  97. )
  98. def test_constructor_errors(self, start, end, match):
  99. with pytest.raises(ValueError, match=match):
  100. BusinessHour(start=start, end=end)
  101. def test_different_normalize_equals(self, _offset):
  102. # GH#21404 changed __eq__ to return False when `normalize` does not match
  103. offset = _offset()
  104. offset2 = _offset(normalize=True)
  105. assert offset != offset2
  106. def test_repr(
  107. self,
  108. offset1,
  109. offset2,
  110. offset3,
  111. offset4,
  112. offset5,
  113. offset6,
  114. offset7,
  115. offset8,
  116. offset9,
  117. offset10,
  118. ):
  119. assert repr(offset1) == "<BusinessHour: BH=09:00-17:00>"
  120. assert repr(offset2) == "<3 * BusinessHours: BH=09:00-17:00>"
  121. assert repr(offset3) == "<-1 * BusinessHour: BH=09:00-17:00>"
  122. assert repr(offset4) == "<-4 * BusinessHours: BH=09:00-17:00>"
  123. assert repr(offset5) == "<BusinessHour: BH=11:00-14:30>"
  124. assert repr(offset6) == "<BusinessHour: BH=20:00-05:00>"
  125. assert repr(offset7) == "<-2 * BusinessHours: BH=21:30-06:30>"
  126. assert repr(offset8) == "<BusinessHour: BH=09:00-12:00,13:00-17:00>"
  127. assert repr(offset9) == "<3 * BusinessHours: BH=09:00-13:00,22:00-03:00>"
  128. assert repr(offset10) == "<-1 * BusinessHour: BH=13:00-17:00,23:00-02:00>"
  129. def test_with_offset(self, dt):
  130. expected = Timestamp("2014-07-01 13:00")
  131. assert dt + BusinessHour() * 3 == expected
  132. assert dt + BusinessHour(n=3) == expected
  133. @pytest.mark.parametrize(
  134. "offset_name",
  135. ["offset1", "offset2", "offset3", "offset4", "offset8", "offset9", "offset10"],
  136. )
  137. def test_eq_attribute(self, offset_name, request):
  138. offset = request.getfixturevalue(offset_name)
  139. assert offset == offset
  140. @pytest.mark.parametrize(
  141. "offset1,offset2",
  142. [
  143. (BusinessHour(start="09:00"), BusinessHour()),
  144. (
  145. BusinessHour(start=["23:00", "13:00"], end=["12:00", "17:00"]),
  146. BusinessHour(start=["13:00", "23:00"], end=["17:00", "12:00"]),
  147. ),
  148. ],
  149. )
  150. def test_eq(self, offset1, offset2):
  151. assert offset1 == offset2
  152. @pytest.mark.parametrize(
  153. "offset1,offset2",
  154. [
  155. (BusinessHour(), BusinessHour(-1)),
  156. (BusinessHour(start="09:00"), BusinessHour(start="09:01")),
  157. (
  158. BusinessHour(start="09:00", end="17:00"),
  159. BusinessHour(start="17:00", end="09:01"),
  160. ),
  161. (
  162. BusinessHour(start=["13:00", "23:00"], end=["18:00", "07:00"]),
  163. BusinessHour(start=["13:00", "23:00"], end=["17:00", "12:00"]),
  164. ),
  165. ],
  166. )
  167. def test_neq(self, offset1, offset2):
  168. assert offset1 != offset2
  169. @pytest.mark.parametrize(
  170. "offset_name",
  171. ["offset1", "offset2", "offset3", "offset4", "offset8", "offset9", "offset10"],
  172. )
  173. def test_hash(self, offset_name, request):
  174. offset = request.getfixturevalue(offset_name)
  175. assert offset == offset
  176. def test_add_datetime(
  177. self,
  178. dt,
  179. offset1,
  180. offset2,
  181. offset3,
  182. offset4,
  183. offset8,
  184. offset9,
  185. offset10,
  186. ):
  187. assert offset1 + dt == datetime(2014, 7, 1, 11)
  188. assert offset2 + dt == datetime(2014, 7, 1, 13)
  189. assert offset3 + dt == datetime(2014, 6, 30, 17)
  190. assert offset4 + dt == datetime(2014, 6, 30, 14)
  191. assert offset8 + dt == datetime(2014, 7, 1, 11)
  192. assert offset9 + dt == datetime(2014, 7, 1, 22)
  193. assert offset10 + dt == datetime(2014, 7, 1, 1)
  194. def test_sub(self, dt, offset2, _offset):
  195. off = offset2
  196. msg = "Cannot subtract datetime from offset"
  197. with pytest.raises(TypeError, match=msg):
  198. off - dt
  199. assert 2 * off - off == off
  200. assert dt - offset2 == dt + _offset(-3)
  201. def test_multiply_by_zero(self, dt, offset1, offset2):
  202. assert dt - 0 * offset1 == dt
  203. assert dt + 0 * offset1 == dt
  204. assert dt - 0 * offset2 == dt
  205. assert dt + 0 * offset2 == dt
  206. def testRollback1(
  207. self,
  208. dt,
  209. _offset,
  210. offset1,
  211. offset2,
  212. offset3,
  213. offset4,
  214. offset5,
  215. offset6,
  216. offset7,
  217. offset8,
  218. offset9,
  219. offset10,
  220. ):
  221. assert offset1.rollback(dt) == dt
  222. assert offset2.rollback(dt) == dt
  223. assert offset3.rollback(dt) == dt
  224. assert offset4.rollback(dt) == dt
  225. assert offset5.rollback(dt) == datetime(2014, 6, 30, 14, 30)
  226. assert offset6.rollback(dt) == datetime(2014, 7, 1, 5, 0)
  227. assert offset7.rollback(dt) == datetime(2014, 7, 1, 6, 30)
  228. assert offset8.rollback(dt) == dt
  229. assert offset9.rollback(dt) == dt
  230. assert offset10.rollback(dt) == datetime(2014, 7, 1, 2)
  231. datet = datetime(2014, 7, 1, 0)
  232. assert offset1.rollback(datet) == datetime(2014, 6, 30, 17)
  233. assert offset2.rollback(datet) == datetime(2014, 6, 30, 17)
  234. assert offset3.rollback(datet) == datetime(2014, 6, 30, 17)
  235. assert offset4.rollback(datet) == datetime(2014, 6, 30, 17)
  236. assert offset5.rollback(datet) == datetime(2014, 6, 30, 14, 30)
  237. assert offset6.rollback(datet) == datet
  238. assert offset7.rollback(datet) == datet
  239. assert offset8.rollback(datet) == datetime(2014, 6, 30, 17)
  240. assert offset9.rollback(datet) == datet
  241. assert offset10.rollback(datet) == datet
  242. assert _offset(5).rollback(dt) == dt
  243. def testRollback2(self, _offset):
  244. assert _offset(-3).rollback(datetime(2014, 7, 5, 15, 0)) == datetime(
  245. 2014, 7, 4, 17, 0
  246. )
  247. def testRollforward1(
  248. self,
  249. dt,
  250. _offset,
  251. offset1,
  252. offset2,
  253. offset3,
  254. offset4,
  255. offset5,
  256. offset6,
  257. offset7,
  258. offset8,
  259. offset9,
  260. offset10,
  261. ):
  262. assert offset1.rollforward(dt) == dt
  263. assert offset2.rollforward(dt) == dt
  264. assert offset3.rollforward(dt) == dt
  265. assert offset4.rollforward(dt) == dt
  266. assert offset5.rollforward(dt) == datetime(2014, 7, 1, 11, 0)
  267. assert offset6.rollforward(dt) == datetime(2014, 7, 1, 20, 0)
  268. assert offset7.rollforward(dt) == datetime(2014, 7, 1, 21, 30)
  269. assert offset8.rollforward(dt) == dt
  270. assert offset9.rollforward(dt) == dt
  271. assert offset10.rollforward(dt) == datetime(2014, 7, 1, 13)
  272. datet = datetime(2014, 7, 1, 0)
  273. assert offset1.rollforward(datet) == datetime(2014, 7, 1, 9)
  274. assert offset2.rollforward(datet) == datetime(2014, 7, 1, 9)
  275. assert offset3.rollforward(datet) == datetime(2014, 7, 1, 9)
  276. assert offset4.rollforward(datet) == datetime(2014, 7, 1, 9)
  277. assert offset5.rollforward(datet) == datetime(2014, 7, 1, 11)
  278. assert offset6.rollforward(datet) == datet
  279. assert offset7.rollforward(datet) == datet
  280. assert offset8.rollforward(datet) == datetime(2014, 7, 1, 9)
  281. assert offset9.rollforward(datet) == datet
  282. assert offset10.rollforward(datet) == datet
  283. assert _offset(5).rollforward(dt) == dt
  284. def testRollforward2(self, _offset):
  285. assert _offset(-3).rollforward(datetime(2014, 7, 5, 16, 0)) == datetime(
  286. 2014, 7, 7, 9
  287. )
  288. def test_roll_date_object(self):
  289. offset = BusinessHour()
  290. dt = datetime(2014, 7, 6, 15, 0)
  291. result = offset.rollback(dt)
  292. assert result == datetime(2014, 7, 4, 17)
  293. result = offset.rollforward(dt)
  294. assert result == datetime(2014, 7, 7, 9)
  295. normalize_cases = []
  296. normalize_cases.append(
  297. (
  298. BusinessHour(normalize=True),
  299. {
  300. datetime(2014, 7, 1, 8): datetime(2014, 7, 1),
  301. datetime(2014, 7, 1, 17): datetime(2014, 7, 2),
  302. datetime(2014, 7, 1, 16): datetime(2014, 7, 2),
  303. datetime(2014, 7, 1, 23): datetime(2014, 7, 2),
  304. datetime(2014, 7, 1, 0): datetime(2014, 7, 1),
  305. datetime(2014, 7, 4, 15): datetime(2014, 7, 4),
  306. datetime(2014, 7, 4, 15, 59): datetime(2014, 7, 4),
  307. datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7),
  308. datetime(2014, 7, 5, 23): datetime(2014, 7, 7),
  309. datetime(2014, 7, 6, 10): datetime(2014, 7, 7),
  310. },
  311. )
  312. )
  313. normalize_cases.append(
  314. (
  315. BusinessHour(-1, normalize=True),
  316. {
  317. datetime(2014, 7, 1, 8): datetime(2014, 6, 30),
  318. datetime(2014, 7, 1, 17): datetime(2014, 7, 1),
  319. datetime(2014, 7, 1, 16): datetime(2014, 7, 1),
  320. datetime(2014, 7, 1, 10): datetime(2014, 6, 30),
  321. datetime(2014, 7, 1, 0): datetime(2014, 6, 30),
  322. datetime(2014, 7, 7, 10): datetime(2014, 7, 4),
  323. datetime(2014, 7, 7, 10, 1): datetime(2014, 7, 7),
  324. datetime(2014, 7, 5, 23): datetime(2014, 7, 4),
  325. datetime(2014, 7, 6, 10): datetime(2014, 7, 4),
  326. },
  327. )
  328. )
  329. normalize_cases.append(
  330. (
  331. BusinessHour(1, normalize=True, start="17:00", end="04:00"),
  332. {
  333. datetime(2014, 7, 1, 8): datetime(2014, 7, 1),
  334. datetime(2014, 7, 1, 17): datetime(2014, 7, 1),
  335. datetime(2014, 7, 1, 23): datetime(2014, 7, 2),
  336. datetime(2014, 7, 2, 2): datetime(2014, 7, 2),
  337. datetime(2014, 7, 2, 3): datetime(2014, 7, 2),
  338. datetime(2014, 7, 4, 23): datetime(2014, 7, 5),
  339. datetime(2014, 7, 5, 2): datetime(2014, 7, 5),
  340. datetime(2014, 7, 7, 2): datetime(2014, 7, 7),
  341. datetime(2014, 7, 7, 17): datetime(2014, 7, 7),
  342. },
  343. )
  344. )
  345. @pytest.mark.parametrize("case", normalize_cases)
  346. def test_normalize(self, case):
  347. offset, cases = case
  348. for dt, expected in cases.items():
  349. assert offset._apply(dt) == expected
  350. on_offset_cases = []
  351. on_offset_cases.append(
  352. (
  353. BusinessHour(),
  354. {
  355. datetime(2014, 7, 1, 9): True,
  356. datetime(2014, 7, 1, 8, 59): False,
  357. datetime(2014, 7, 1, 8): False,
  358. datetime(2014, 7, 1, 17): True,
  359. datetime(2014, 7, 1, 17, 1): False,
  360. datetime(2014, 7, 1, 18): False,
  361. datetime(2014, 7, 5, 9): False,
  362. datetime(2014, 7, 6, 12): False,
  363. },
  364. )
  365. )
  366. on_offset_cases.append(
  367. (
  368. BusinessHour(start="10:00", end="15:00"),
  369. {
  370. datetime(2014, 7, 1, 9): False,
  371. datetime(2014, 7, 1, 10): True,
  372. datetime(2014, 7, 1, 15): True,
  373. datetime(2014, 7, 1, 15, 1): False,
  374. datetime(2014, 7, 5, 12): False,
  375. datetime(2014, 7, 6, 12): False,
  376. },
  377. )
  378. )
  379. on_offset_cases.append(
  380. (
  381. BusinessHour(start="19:00", end="05:00"),
  382. {
  383. datetime(2014, 7, 1, 9, 0): False,
  384. datetime(2014, 7, 1, 10, 0): False,
  385. datetime(2014, 7, 1, 15): False,
  386. datetime(2014, 7, 1, 15, 1): False,
  387. datetime(2014, 7, 5, 12, 0): False,
  388. datetime(2014, 7, 6, 12, 0): False,
  389. datetime(2014, 7, 1, 19, 0): True,
  390. datetime(2014, 7, 2, 0, 0): True,
  391. datetime(2014, 7, 4, 23): True,
  392. datetime(2014, 7, 5, 1): True,
  393. datetime(2014, 7, 5, 5, 0): True,
  394. datetime(2014, 7, 6, 23, 0): False,
  395. datetime(2014, 7, 7, 3, 0): False,
  396. },
  397. )
  398. )
  399. on_offset_cases.append(
  400. (
  401. BusinessHour(start=["09:00", "13:00"], end=["12:00", "17:00"]),
  402. {
  403. datetime(2014, 7, 1, 9): True,
  404. datetime(2014, 7, 1, 8, 59): False,
  405. datetime(2014, 7, 1, 8): False,
  406. datetime(2014, 7, 1, 17): True,
  407. datetime(2014, 7, 1, 17, 1): False,
  408. datetime(2014, 7, 1, 18): False,
  409. datetime(2014, 7, 5, 9): False,
  410. datetime(2014, 7, 6, 12): False,
  411. datetime(2014, 7, 1, 12, 30): False,
  412. },
  413. )
  414. )
  415. on_offset_cases.append(
  416. (
  417. BusinessHour(start=["19:00", "23:00"], end=["21:00", "05:00"]),
  418. {
  419. datetime(2014, 7, 1, 9, 0): False,
  420. datetime(2014, 7, 1, 10, 0): False,
  421. datetime(2014, 7, 1, 15): False,
  422. datetime(2014, 7, 1, 15, 1): False,
  423. datetime(2014, 7, 5, 12, 0): False,
  424. datetime(2014, 7, 6, 12, 0): False,
  425. datetime(2014, 7, 1, 19, 0): True,
  426. datetime(2014, 7, 2, 0, 0): True,
  427. datetime(2014, 7, 4, 23): True,
  428. datetime(2014, 7, 5, 1): True,
  429. datetime(2014, 7, 5, 5, 0): True,
  430. datetime(2014, 7, 6, 23, 0): False,
  431. datetime(2014, 7, 7, 3, 0): False,
  432. datetime(2014, 7, 4, 22): False,
  433. },
  434. )
  435. )
  436. @pytest.mark.parametrize("case", on_offset_cases)
  437. def test_is_on_offset(self, case):
  438. offset, cases = case
  439. for dt, expected in cases.items():
  440. assert offset.is_on_offset(dt) == expected
  441. apply_cases = [
  442. (
  443. BusinessHour(),
  444. {
  445. datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 12),
  446. datetime(2014, 7, 1, 13): datetime(2014, 7, 1, 14),
  447. datetime(2014, 7, 1, 15): datetime(2014, 7, 1, 16),
  448. datetime(2014, 7, 1, 19): datetime(2014, 7, 2, 10),
  449. datetime(2014, 7, 1, 16): datetime(2014, 7, 2, 9),
  450. datetime(2014, 7, 1, 16, 30, 15): datetime(2014, 7, 2, 9, 30, 15),
  451. datetime(2014, 7, 1, 17): datetime(2014, 7, 2, 10),
  452. datetime(2014, 7, 2, 11): datetime(2014, 7, 2, 12),
  453. # out of business hours
  454. datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 10),
  455. datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 10),
  456. datetime(2014, 7, 2, 23): datetime(2014, 7, 3, 10),
  457. datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 10),
  458. # saturday
  459. datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 10),
  460. datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 10),
  461. datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7, 9, 30),
  462. datetime(2014, 7, 4, 16, 30, 30): datetime(2014, 7, 7, 9, 30, 30),
  463. },
  464. ),
  465. (
  466. BusinessHour(4),
  467. {
  468. datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 15),
  469. datetime(2014, 7, 1, 13): datetime(2014, 7, 2, 9),
  470. datetime(2014, 7, 1, 15): datetime(2014, 7, 2, 11),
  471. datetime(2014, 7, 1, 16): datetime(2014, 7, 2, 12),
  472. datetime(2014, 7, 1, 17): datetime(2014, 7, 2, 13),
  473. datetime(2014, 7, 2, 11): datetime(2014, 7, 2, 15),
  474. datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 13),
  475. datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 13),
  476. datetime(2014, 7, 2, 23): datetime(2014, 7, 3, 13),
  477. datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 13),
  478. datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 13),
  479. datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 13),
  480. datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7, 12, 30),
  481. datetime(2014, 7, 4, 16, 30, 30): datetime(2014, 7, 7, 12, 30, 30),
  482. },
  483. ),
  484. (
  485. BusinessHour(-1),
  486. {
  487. datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 10),
  488. datetime(2014, 7, 1, 13): datetime(2014, 7, 1, 12),
  489. datetime(2014, 7, 1, 15): datetime(2014, 7, 1, 14),
  490. datetime(2014, 7, 1, 16): datetime(2014, 7, 1, 15),
  491. datetime(2014, 7, 1, 10): datetime(2014, 6, 30, 17),
  492. datetime(2014, 7, 1, 16, 30, 15): datetime(2014, 7, 1, 15, 30, 15),
  493. datetime(2014, 7, 1, 9, 30, 15): datetime(2014, 6, 30, 16, 30, 15),
  494. datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 16),
  495. datetime(2014, 7, 1, 5): datetime(2014, 6, 30, 16),
  496. datetime(2014, 7, 2, 11): datetime(2014, 7, 2, 10),
  497. # out of business hours
  498. datetime(2014, 7, 2, 8): datetime(2014, 7, 1, 16),
  499. datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 16),
  500. datetime(2014, 7, 2, 23): datetime(2014, 7, 2, 16),
  501. datetime(2014, 7, 3, 0): datetime(2014, 7, 2, 16),
  502. # saturday
  503. datetime(2014, 7, 5, 15): datetime(2014, 7, 4, 16),
  504. datetime(2014, 7, 7, 9): datetime(2014, 7, 4, 16),
  505. datetime(2014, 7, 7, 9, 30): datetime(2014, 7, 4, 16, 30),
  506. datetime(2014, 7, 7, 9, 30, 30): datetime(2014, 7, 4, 16, 30, 30),
  507. },
  508. ),
  509. (
  510. BusinessHour(-4),
  511. {
  512. datetime(2014, 7, 1, 11): datetime(2014, 6, 30, 15),
  513. datetime(2014, 7, 1, 13): datetime(2014, 6, 30, 17),
  514. datetime(2014, 7, 1, 15): datetime(2014, 7, 1, 11),
  515. datetime(2014, 7, 1, 16): datetime(2014, 7, 1, 12),
  516. datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 13),
  517. datetime(2014, 7, 2, 11): datetime(2014, 7, 1, 15),
  518. datetime(2014, 7, 2, 8): datetime(2014, 7, 1, 13),
  519. datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 13),
  520. datetime(2014, 7, 2, 23): datetime(2014, 7, 2, 13),
  521. datetime(2014, 7, 3, 0): datetime(2014, 7, 2, 13),
  522. datetime(2014, 7, 5, 15): datetime(2014, 7, 4, 13),
  523. datetime(2014, 7, 4, 18): datetime(2014, 7, 4, 13),
  524. datetime(2014, 7, 7, 9, 30): datetime(2014, 7, 4, 13, 30),
  525. datetime(2014, 7, 7, 9, 30, 30): datetime(2014, 7, 4, 13, 30, 30),
  526. },
  527. ),
  528. (
  529. BusinessHour(start="13:00", end="16:00"),
  530. {
  531. datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 14),
  532. datetime(2014, 7, 1, 13): datetime(2014, 7, 1, 14),
  533. datetime(2014, 7, 1, 15): datetime(2014, 7, 2, 13),
  534. datetime(2014, 7, 1, 19): datetime(2014, 7, 2, 14),
  535. datetime(2014, 7, 1, 16): datetime(2014, 7, 2, 14),
  536. datetime(2014, 7, 1, 15, 30, 15): datetime(2014, 7, 2, 13, 30, 15),
  537. datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 14),
  538. datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 14),
  539. },
  540. ),
  541. (
  542. BusinessHour(n=2, start="13:00", end="16:00"),
  543. {
  544. datetime(2014, 7, 1, 17): datetime(2014, 7, 2, 15),
  545. datetime(2014, 7, 2, 14): datetime(2014, 7, 3, 13),
  546. datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 15),
  547. datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 15),
  548. datetime(2014, 7, 2, 14, 30): datetime(2014, 7, 3, 13, 30),
  549. datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 15),
  550. datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 15),
  551. datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 15),
  552. datetime(2014, 7, 4, 14, 30): datetime(2014, 7, 7, 13, 30),
  553. datetime(2014, 7, 4, 14, 30, 30): datetime(2014, 7, 7, 13, 30, 30),
  554. },
  555. ),
  556. (
  557. BusinessHour(n=-1, start="13:00", end="16:00"),
  558. {
  559. datetime(2014, 7, 2, 11): datetime(2014, 7, 1, 15),
  560. datetime(2014, 7, 2, 13): datetime(2014, 7, 1, 15),
  561. datetime(2014, 7, 2, 14): datetime(2014, 7, 1, 16),
  562. datetime(2014, 7, 2, 15): datetime(2014, 7, 2, 14),
  563. datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 15),
  564. datetime(2014, 7, 2, 16): datetime(2014, 7, 2, 15),
  565. datetime(2014, 7, 2, 13, 30, 15): datetime(2014, 7, 1, 15, 30, 15),
  566. datetime(2014, 7, 5, 15): datetime(2014, 7, 4, 15),
  567. datetime(2014, 7, 7, 11): datetime(2014, 7, 4, 15),
  568. },
  569. ),
  570. (
  571. BusinessHour(n=-3, start="10:00", end="16:00"),
  572. {
  573. datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 13),
  574. datetime(2014, 7, 2, 14): datetime(2014, 7, 2, 11),
  575. datetime(2014, 7, 2, 8): datetime(2014, 7, 1, 13),
  576. datetime(2014, 7, 2, 13): datetime(2014, 7, 1, 16),
  577. datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 13),
  578. datetime(2014, 7, 2, 11, 30): datetime(2014, 7, 1, 14, 30),
  579. datetime(2014, 7, 3, 0): datetime(2014, 7, 2, 13),
  580. datetime(2014, 7, 4, 10): datetime(2014, 7, 3, 13),
  581. datetime(2014, 7, 5, 15): datetime(2014, 7, 4, 13),
  582. datetime(2014, 7, 4, 16): datetime(2014, 7, 4, 13),
  583. datetime(2014, 7, 4, 12, 30): datetime(2014, 7, 3, 15, 30),
  584. datetime(2014, 7, 4, 12, 30, 30): datetime(2014, 7, 3, 15, 30, 30),
  585. },
  586. ),
  587. (
  588. BusinessHour(start="19:00", end="05:00"),
  589. {
  590. datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 20),
  591. datetime(2014, 7, 2, 14): datetime(2014, 7, 2, 20),
  592. datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 20),
  593. datetime(2014, 7, 2, 13): datetime(2014, 7, 2, 20),
  594. datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 20),
  595. datetime(2014, 7, 2, 4, 30): datetime(2014, 7, 2, 19, 30),
  596. datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 1),
  597. datetime(2014, 7, 4, 10): datetime(2014, 7, 4, 20),
  598. datetime(2014, 7, 4, 23): datetime(2014, 7, 5, 0),
  599. datetime(2014, 7, 5, 0): datetime(2014, 7, 5, 1),
  600. datetime(2014, 7, 5, 4): datetime(2014, 7, 7, 19),
  601. datetime(2014, 7, 5, 4, 30): datetime(2014, 7, 7, 19, 30),
  602. datetime(2014, 7, 5, 4, 30, 30): datetime(2014, 7, 7, 19, 30, 30),
  603. },
  604. ),
  605. (
  606. BusinessHour(n=-1, start="19:00", end="05:00"),
  607. {
  608. datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 4),
  609. datetime(2014, 7, 2, 14): datetime(2014, 7, 2, 4),
  610. datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 4),
  611. datetime(2014, 7, 2, 13): datetime(2014, 7, 2, 4),
  612. datetime(2014, 7, 2, 20): datetime(2014, 7, 2, 5),
  613. datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 4),
  614. datetime(2014, 7, 2, 19, 30): datetime(2014, 7, 2, 4, 30),
  615. datetime(2014, 7, 3, 0): datetime(2014, 7, 2, 23),
  616. datetime(2014, 7, 3, 6): datetime(2014, 7, 3, 4),
  617. datetime(2014, 7, 4, 23): datetime(2014, 7, 4, 22),
  618. datetime(2014, 7, 5, 0): datetime(2014, 7, 4, 23),
  619. datetime(2014, 7, 5, 4): datetime(2014, 7, 5, 3),
  620. datetime(2014, 7, 7, 19, 30): datetime(2014, 7, 5, 4, 30),
  621. datetime(2014, 7, 7, 19, 30, 30): datetime(2014, 7, 5, 4, 30, 30),
  622. },
  623. ),
  624. (
  625. BusinessHour(n=4, start="00:00", end="23:00"),
  626. {
  627. datetime(2014, 7, 3, 22): datetime(2014, 7, 4, 3),
  628. datetime(2014, 7, 4, 22): datetime(2014, 7, 7, 3),
  629. datetime(2014, 7, 3, 22, 30): datetime(2014, 7, 4, 3, 30),
  630. datetime(2014, 7, 3, 22, 20): datetime(2014, 7, 4, 3, 20),
  631. datetime(2014, 7, 4, 22, 30, 30): datetime(2014, 7, 7, 3, 30, 30),
  632. datetime(2014, 7, 4, 22, 30, 20): datetime(2014, 7, 7, 3, 30, 20),
  633. },
  634. ),
  635. (
  636. BusinessHour(n=-4, start="00:00", end="23:00"),
  637. {
  638. datetime(2014, 7, 4, 3): datetime(2014, 7, 3, 22),
  639. datetime(2014, 7, 7, 3): datetime(2014, 7, 4, 22),
  640. datetime(2014, 7, 4, 3, 30): datetime(2014, 7, 3, 22, 30),
  641. datetime(2014, 7, 4, 3, 20): datetime(2014, 7, 3, 22, 20),
  642. datetime(2014, 7, 7, 3, 30, 30): datetime(2014, 7, 4, 22, 30, 30),
  643. datetime(2014, 7, 7, 3, 30, 20): datetime(2014, 7, 4, 22, 30, 20),
  644. },
  645. ),
  646. (
  647. BusinessHour(start=["09:00", "14:00"], end=["12:00", "18:00"]),
  648. {
  649. datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 14),
  650. datetime(2014, 7, 1, 15): datetime(2014, 7, 1, 16),
  651. datetime(2014, 7, 1, 19): datetime(2014, 7, 2, 10),
  652. datetime(2014, 7, 1, 16): datetime(2014, 7, 1, 17),
  653. datetime(2014, 7, 1, 16, 30, 15): datetime(2014, 7, 1, 17, 30, 15),
  654. datetime(2014, 7, 1, 17): datetime(2014, 7, 2, 9),
  655. datetime(2014, 7, 2, 11): datetime(2014, 7, 2, 14),
  656. # out of business hours
  657. datetime(2014, 7, 1, 13): datetime(2014, 7, 1, 15),
  658. datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 10),
  659. datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 10),
  660. datetime(2014, 7, 2, 23): datetime(2014, 7, 3, 10),
  661. datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 10),
  662. # saturday
  663. datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 10),
  664. datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 9),
  665. datetime(2014, 7, 4, 17, 30): datetime(2014, 7, 7, 9, 30),
  666. datetime(2014, 7, 4, 17, 30, 30): datetime(2014, 7, 7, 9, 30, 30),
  667. },
  668. ),
  669. (
  670. BusinessHour(n=4, start=["09:00", "14:00"], end=["12:00", "18:00"]),
  671. {
  672. datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 17),
  673. datetime(2014, 7, 1, 13): datetime(2014, 7, 2, 9),
  674. datetime(2014, 7, 1, 15): datetime(2014, 7, 2, 10),
  675. datetime(2014, 7, 1, 16): datetime(2014, 7, 2, 11),
  676. datetime(2014, 7, 1, 17): datetime(2014, 7, 2, 14),
  677. datetime(2014, 7, 2, 11): datetime(2014, 7, 2, 17),
  678. datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 15),
  679. datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 15),
  680. datetime(2014, 7, 2, 23): datetime(2014, 7, 3, 15),
  681. datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 15),
  682. datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 15),
  683. datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 14),
  684. datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7, 11, 30),
  685. datetime(2014, 7, 4, 16, 30, 30): datetime(2014, 7, 7, 11, 30, 30),
  686. },
  687. ),
  688. (
  689. BusinessHour(n=-4, start=["09:00", "14:00"], end=["12:00", "18:00"]),
  690. {
  691. datetime(2014, 7, 1, 11): datetime(2014, 6, 30, 16),
  692. datetime(2014, 7, 1, 13): datetime(2014, 6, 30, 17),
  693. datetime(2014, 7, 1, 15): datetime(2014, 6, 30, 18),
  694. datetime(2014, 7, 1, 16): datetime(2014, 7, 1, 10),
  695. datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 11),
  696. datetime(2014, 7, 2, 11): datetime(2014, 7, 1, 16),
  697. datetime(2014, 7, 2, 8): datetime(2014, 7, 1, 12),
  698. datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 12),
  699. datetime(2014, 7, 2, 23): datetime(2014, 7, 2, 12),
  700. datetime(2014, 7, 3, 0): datetime(2014, 7, 2, 12),
  701. datetime(2014, 7, 5, 15): datetime(2014, 7, 4, 12),
  702. datetime(2014, 7, 4, 18): datetime(2014, 7, 4, 12),
  703. datetime(2014, 7, 7, 9, 30): datetime(2014, 7, 4, 14, 30),
  704. datetime(2014, 7, 7, 9, 30, 30): datetime(2014, 7, 4, 14, 30, 30),
  705. },
  706. ),
  707. (
  708. BusinessHour(n=-1, start=["19:00", "03:00"], end=["01:00", "05:00"]),
  709. {
  710. datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 4),
  711. datetime(2014, 7, 2, 14): datetime(2014, 7, 2, 4),
  712. datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 4),
  713. datetime(2014, 7, 2, 13): datetime(2014, 7, 2, 4),
  714. datetime(2014, 7, 2, 20): datetime(2014, 7, 2, 5),
  715. datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 4),
  716. datetime(2014, 7, 2, 4): datetime(2014, 7, 2, 1),
  717. datetime(2014, 7, 2, 19, 30): datetime(2014, 7, 2, 4, 30),
  718. datetime(2014, 7, 3, 0): datetime(2014, 7, 2, 23),
  719. datetime(2014, 7, 3, 6): datetime(2014, 7, 3, 4),
  720. datetime(2014, 7, 4, 23): datetime(2014, 7, 4, 22),
  721. datetime(2014, 7, 5, 0): datetime(2014, 7, 4, 23),
  722. datetime(2014, 7, 5, 4): datetime(2014, 7, 5, 0),
  723. datetime(2014, 7, 7, 3, 30): datetime(2014, 7, 5, 0, 30),
  724. datetime(2014, 7, 7, 19, 30): datetime(2014, 7, 7, 4, 30),
  725. datetime(2014, 7, 7, 19, 30, 30): datetime(2014, 7, 7, 4, 30, 30),
  726. },
  727. ),
  728. ]
  729. # long business hours (see gh-26381)
  730. # multiple business hours
  731. @pytest.mark.parametrize("case", apply_cases)
  732. def test_apply(self, case):
  733. offset, cases = case
  734. for base, expected in cases.items():
  735. assert_offset_equal(offset, base, expected)
  736. apply_large_n_cases = [
  737. (
  738. # A week later
  739. BusinessHour(40),
  740. {
  741. datetime(2014, 7, 1, 11): datetime(2014, 7, 8, 11),
  742. datetime(2014, 7, 1, 13): datetime(2014, 7, 8, 13),
  743. datetime(2014, 7, 1, 15): datetime(2014, 7, 8, 15),
  744. datetime(2014, 7, 1, 16): datetime(2014, 7, 8, 16),
  745. datetime(2014, 7, 1, 17): datetime(2014, 7, 9, 9),
  746. datetime(2014, 7, 2, 11): datetime(2014, 7, 9, 11),
  747. datetime(2014, 7, 2, 8): datetime(2014, 7, 9, 9),
  748. datetime(2014, 7, 2, 19): datetime(2014, 7, 10, 9),
  749. datetime(2014, 7, 2, 23): datetime(2014, 7, 10, 9),
  750. datetime(2014, 7, 3, 0): datetime(2014, 7, 10, 9),
  751. datetime(2014, 7, 5, 15): datetime(2014, 7, 14, 9),
  752. datetime(2014, 7, 4, 18): datetime(2014, 7, 14, 9),
  753. datetime(2014, 7, 7, 9, 30): datetime(2014, 7, 14, 9, 30),
  754. datetime(2014, 7, 7, 9, 30, 30): datetime(2014, 7, 14, 9, 30, 30),
  755. },
  756. ),
  757. (
  758. # 3 days and 1 hour before
  759. BusinessHour(-25),
  760. {
  761. datetime(2014, 7, 1, 11): datetime(2014, 6, 26, 10),
  762. datetime(2014, 7, 1, 13): datetime(2014, 6, 26, 12),
  763. datetime(2014, 7, 1, 9): datetime(2014, 6, 25, 16),
  764. datetime(2014, 7, 1, 10): datetime(2014, 6, 25, 17),
  765. datetime(2014, 7, 3, 11): datetime(2014, 6, 30, 10),
  766. datetime(2014, 7, 3, 8): datetime(2014, 6, 27, 16),
  767. datetime(2014, 7, 3, 19): datetime(2014, 6, 30, 16),
  768. datetime(2014, 7, 3, 23): datetime(2014, 6, 30, 16),
  769. datetime(2014, 7, 4, 9): datetime(2014, 6, 30, 16),
  770. datetime(2014, 7, 5, 15): datetime(2014, 7, 1, 16),
  771. datetime(2014, 7, 6, 18): datetime(2014, 7, 1, 16),
  772. datetime(2014, 7, 7, 9, 30): datetime(2014, 7, 1, 16, 30),
  773. datetime(2014, 7, 7, 10, 30, 30): datetime(2014, 7, 2, 9, 30, 30),
  774. },
  775. ),
  776. (
  777. # 5 days and 3 hours later
  778. BusinessHour(28, start="21:00", end="02:00"),
  779. {
  780. datetime(2014, 7, 1, 11): datetime(2014, 7, 9, 0),
  781. datetime(2014, 7, 1, 22): datetime(2014, 7, 9, 1),
  782. datetime(2014, 7, 1, 23): datetime(2014, 7, 9, 21),
  783. datetime(2014, 7, 2, 2): datetime(2014, 7, 10, 0),
  784. datetime(2014, 7, 3, 21): datetime(2014, 7, 11, 0),
  785. datetime(2014, 7, 4, 1): datetime(2014, 7, 11, 23),
  786. datetime(2014, 7, 4, 2): datetime(2014, 7, 12, 0),
  787. datetime(2014, 7, 4, 3): datetime(2014, 7, 12, 0),
  788. datetime(2014, 7, 5, 1): datetime(2014, 7, 14, 23),
  789. datetime(2014, 7, 5, 15): datetime(2014, 7, 15, 0),
  790. datetime(2014, 7, 6, 18): datetime(2014, 7, 15, 0),
  791. datetime(2014, 7, 7, 1): datetime(2014, 7, 15, 0),
  792. datetime(2014, 7, 7, 23, 30): datetime(2014, 7, 15, 21, 30),
  793. },
  794. ),
  795. (
  796. # large n for multiple opening hours (3 days and 1 hour before)
  797. BusinessHour(n=-25, start=["09:00", "14:00"], end=["12:00", "19:00"]),
  798. {
  799. datetime(2014, 7, 1, 11): datetime(2014, 6, 26, 10),
  800. datetime(2014, 7, 1, 13): datetime(2014, 6, 26, 11),
  801. datetime(2014, 7, 1, 9): datetime(2014, 6, 25, 18),
  802. datetime(2014, 7, 1, 10): datetime(2014, 6, 25, 19),
  803. datetime(2014, 7, 3, 11): datetime(2014, 6, 30, 10),
  804. datetime(2014, 7, 3, 8): datetime(2014, 6, 27, 18),
  805. datetime(2014, 7, 3, 19): datetime(2014, 6, 30, 18),
  806. datetime(2014, 7, 3, 23): datetime(2014, 6, 30, 18),
  807. datetime(2014, 7, 4, 9): datetime(2014, 6, 30, 18),
  808. datetime(2014, 7, 5, 15): datetime(2014, 7, 1, 18),
  809. datetime(2014, 7, 6, 18): datetime(2014, 7, 1, 18),
  810. datetime(2014, 7, 7, 9, 30): datetime(2014, 7, 1, 18, 30),
  811. datetime(2014, 7, 7, 10, 30, 30): datetime(2014, 7, 2, 9, 30, 30),
  812. },
  813. ),
  814. (
  815. # 5 days and 3 hours later
  816. BusinessHour(28, start=["21:00", "03:00"], end=["01:00", "04:00"]),
  817. {
  818. datetime(2014, 7, 1, 11): datetime(2014, 7, 9, 0),
  819. datetime(2014, 7, 1, 22): datetime(2014, 7, 9, 3),
  820. datetime(2014, 7, 1, 23): datetime(2014, 7, 9, 21),
  821. datetime(2014, 7, 2, 2): datetime(2014, 7, 9, 23),
  822. datetime(2014, 7, 3, 21): datetime(2014, 7, 11, 0),
  823. datetime(2014, 7, 4, 1): datetime(2014, 7, 11, 23),
  824. datetime(2014, 7, 4, 2): datetime(2014, 7, 11, 23),
  825. datetime(2014, 7, 4, 3): datetime(2014, 7, 11, 23),
  826. datetime(2014, 7, 4, 21): datetime(2014, 7, 12, 0),
  827. datetime(2014, 7, 5, 0): datetime(2014, 7, 14, 22),
  828. datetime(2014, 7, 5, 1): datetime(2014, 7, 14, 23),
  829. datetime(2014, 7, 5, 15): datetime(2014, 7, 14, 23),
  830. datetime(2014, 7, 6, 18): datetime(2014, 7, 14, 23),
  831. datetime(2014, 7, 7, 1): datetime(2014, 7, 14, 23),
  832. datetime(2014, 7, 7, 23, 30): datetime(2014, 7, 15, 21, 30),
  833. },
  834. ),
  835. ]
  836. @pytest.mark.parametrize("case", apply_large_n_cases)
  837. def test_apply_large_n(self, case):
  838. offset, cases = case
  839. for base, expected in cases.items():
  840. assert_offset_equal(offset, base, expected)
  841. def test_apply_nanoseconds(self):
  842. tests = [
  843. (
  844. BusinessHour(),
  845. {
  846. Timestamp("2014-07-04 15:00")
  847. + Nano(5): Timestamp("2014-07-04 16:00")
  848. + Nano(5),
  849. Timestamp("2014-07-04 16:00")
  850. + Nano(5): Timestamp("2014-07-07 09:00")
  851. + Nano(5),
  852. Timestamp("2014-07-04 16:00")
  853. - Nano(5): Timestamp("2014-07-04 17:00")
  854. - Nano(5),
  855. },
  856. ),
  857. (
  858. BusinessHour(-1),
  859. {
  860. Timestamp("2014-07-04 15:00")
  861. + Nano(5): Timestamp("2014-07-04 14:00")
  862. + Nano(5),
  863. Timestamp("2014-07-04 10:00")
  864. + Nano(5): Timestamp("2014-07-04 09:00")
  865. + Nano(5),
  866. Timestamp("2014-07-04 10:00")
  867. - Nano(5): Timestamp("2014-07-03 17:00")
  868. - Nano(5),
  869. },
  870. ),
  871. ]
  872. for offset, cases in tests:
  873. for base, expected in cases.items():
  874. assert_offset_equal(offset, base, expected)
  875. def test_datetimeindex(self):
  876. idx1 = date_range(start="2014-07-04 15:00", end="2014-07-08 10:00", freq="BH")
  877. idx2 = date_range(start="2014-07-04 15:00", periods=12, freq="BH")
  878. idx3 = date_range(end="2014-07-08 10:00", periods=12, freq="BH")
  879. expected = DatetimeIndex(
  880. [
  881. "2014-07-04 15:00",
  882. "2014-07-04 16:00",
  883. "2014-07-07 09:00",
  884. "2014-07-07 10:00",
  885. "2014-07-07 11:00",
  886. "2014-07-07 12:00",
  887. "2014-07-07 13:00",
  888. "2014-07-07 14:00",
  889. "2014-07-07 15:00",
  890. "2014-07-07 16:00",
  891. "2014-07-08 09:00",
  892. "2014-07-08 10:00",
  893. ],
  894. freq="BH",
  895. )
  896. for idx in [idx1, idx2, idx3]:
  897. tm.assert_index_equal(idx, expected)
  898. idx1 = date_range(start="2014-07-04 15:45", end="2014-07-08 10:45", freq="BH")
  899. idx2 = date_range(start="2014-07-04 15:45", periods=12, freq="BH")
  900. idx3 = date_range(end="2014-07-08 10:45", periods=12, freq="BH")
  901. expected = idx1
  902. for idx in [idx1, idx2, idx3]:
  903. tm.assert_index_equal(idx, expected)
  904. def test_short_datetimeindex_creation(self):
  905. # gh-49835
  906. idx4 = date_range(start="2014-07-01 10:00", freq="BH", periods=1)
  907. expected4 = DatetimeIndex(["2014-07-01 10:00"], freq="BH")
  908. tm.assert_index_equal(idx4, expected4)
  909. def test_bday_ignores_timedeltas(self):
  910. idx = date_range("2010/02/01", "2010/02/10", freq="12H")
  911. t1 = idx + BDay(offset=Timedelta(3, unit="H"))
  912. expected = DatetimeIndex(
  913. [
  914. "2010-02-02 03:00:00",
  915. "2010-02-02 15:00:00",
  916. "2010-02-03 03:00:00",
  917. "2010-02-03 15:00:00",
  918. "2010-02-04 03:00:00",
  919. "2010-02-04 15:00:00",
  920. "2010-02-05 03:00:00",
  921. "2010-02-05 15:00:00",
  922. "2010-02-08 03:00:00",
  923. "2010-02-08 15:00:00",
  924. "2010-02-08 03:00:00",
  925. "2010-02-08 15:00:00",
  926. "2010-02-08 03:00:00",
  927. "2010-02-08 15:00:00",
  928. "2010-02-09 03:00:00",
  929. "2010-02-09 15:00:00",
  930. "2010-02-10 03:00:00",
  931. "2010-02-10 15:00:00",
  932. "2010-02-11 03:00:00",
  933. ],
  934. freq=None,
  935. )
  936. tm.assert_index_equal(t1, expected)
  937. class TestOpeningTimes:
  938. # opening time should be affected by sign of n, not by n's value and end
  939. opening_time_cases = [
  940. (
  941. [
  942. BusinessHour(),
  943. BusinessHour(n=2),
  944. BusinessHour(n=4),
  945. BusinessHour(end="10:00"),
  946. BusinessHour(n=2, end="4:00"),
  947. BusinessHour(n=4, end="15:00"),
  948. ],
  949. {
  950. datetime(2014, 7, 1, 11): (
  951. datetime(2014, 7, 2, 9),
  952. datetime(2014, 7, 1, 9),
  953. ),
  954. datetime(2014, 7, 1, 18): (
  955. datetime(2014, 7, 2, 9),
  956. datetime(2014, 7, 1, 9),
  957. ),
  958. datetime(2014, 7, 1, 23): (
  959. datetime(2014, 7, 2, 9),
  960. datetime(2014, 7, 1, 9),
  961. ),
  962. datetime(2014, 7, 2, 8): (
  963. datetime(2014, 7, 2, 9),
  964. datetime(2014, 7, 1, 9),
  965. ),
  966. # if timestamp is on opening time, next opening time is
  967. # as it is
  968. datetime(2014, 7, 2, 9): (
  969. datetime(2014, 7, 2, 9),
  970. datetime(2014, 7, 2, 9),
  971. ),
  972. datetime(2014, 7, 2, 10): (
  973. datetime(2014, 7, 3, 9),
  974. datetime(2014, 7, 2, 9),
  975. ),
  976. # 2014-07-05 is saturday
  977. datetime(2014, 7, 5, 10): (
  978. datetime(2014, 7, 7, 9),
  979. datetime(2014, 7, 4, 9),
  980. ),
  981. datetime(2014, 7, 4, 10): (
  982. datetime(2014, 7, 7, 9),
  983. datetime(2014, 7, 4, 9),
  984. ),
  985. datetime(2014, 7, 4, 23): (
  986. datetime(2014, 7, 7, 9),
  987. datetime(2014, 7, 4, 9),
  988. ),
  989. datetime(2014, 7, 6, 10): (
  990. datetime(2014, 7, 7, 9),
  991. datetime(2014, 7, 4, 9),
  992. ),
  993. datetime(2014, 7, 7, 5): (
  994. datetime(2014, 7, 7, 9),
  995. datetime(2014, 7, 4, 9),
  996. ),
  997. datetime(2014, 7, 7, 9, 1): (
  998. datetime(2014, 7, 8, 9),
  999. datetime(2014, 7, 7, 9),
  1000. ),
  1001. },
  1002. ),
  1003. (
  1004. [
  1005. BusinessHour(start="11:15"),
  1006. BusinessHour(n=2, start="11:15"),
  1007. BusinessHour(n=3, start="11:15"),
  1008. BusinessHour(start="11:15", end="10:00"),
  1009. BusinessHour(n=2, start="11:15", end="4:00"),
  1010. BusinessHour(n=3, start="11:15", end="15:00"),
  1011. ],
  1012. {
  1013. datetime(2014, 7, 1, 11): (
  1014. datetime(2014, 7, 1, 11, 15),
  1015. datetime(2014, 6, 30, 11, 15),
  1016. ),
  1017. datetime(2014, 7, 1, 18): (
  1018. datetime(2014, 7, 2, 11, 15),
  1019. datetime(2014, 7, 1, 11, 15),
  1020. ),
  1021. datetime(2014, 7, 1, 23): (
  1022. datetime(2014, 7, 2, 11, 15),
  1023. datetime(2014, 7, 1, 11, 15),
  1024. ),
  1025. datetime(2014, 7, 2, 8): (
  1026. datetime(2014, 7, 2, 11, 15),
  1027. datetime(2014, 7, 1, 11, 15),
  1028. ),
  1029. datetime(2014, 7, 2, 9): (
  1030. datetime(2014, 7, 2, 11, 15),
  1031. datetime(2014, 7, 1, 11, 15),
  1032. ),
  1033. datetime(2014, 7, 2, 10): (
  1034. datetime(2014, 7, 2, 11, 15),
  1035. datetime(2014, 7, 1, 11, 15),
  1036. ),
  1037. datetime(2014, 7, 2, 11, 15): (
  1038. datetime(2014, 7, 2, 11, 15),
  1039. datetime(2014, 7, 2, 11, 15),
  1040. ),
  1041. datetime(2014, 7, 2, 11, 15, 1): (
  1042. datetime(2014, 7, 3, 11, 15),
  1043. datetime(2014, 7, 2, 11, 15),
  1044. ),
  1045. datetime(2014, 7, 5, 10): (
  1046. datetime(2014, 7, 7, 11, 15),
  1047. datetime(2014, 7, 4, 11, 15),
  1048. ),
  1049. datetime(2014, 7, 4, 10): (
  1050. datetime(2014, 7, 4, 11, 15),
  1051. datetime(2014, 7, 3, 11, 15),
  1052. ),
  1053. datetime(2014, 7, 4, 23): (
  1054. datetime(2014, 7, 7, 11, 15),
  1055. datetime(2014, 7, 4, 11, 15),
  1056. ),
  1057. datetime(2014, 7, 6, 10): (
  1058. datetime(2014, 7, 7, 11, 15),
  1059. datetime(2014, 7, 4, 11, 15),
  1060. ),
  1061. datetime(2014, 7, 7, 5): (
  1062. datetime(2014, 7, 7, 11, 15),
  1063. datetime(2014, 7, 4, 11, 15),
  1064. ),
  1065. datetime(2014, 7, 7, 9, 1): (
  1066. datetime(2014, 7, 7, 11, 15),
  1067. datetime(2014, 7, 4, 11, 15),
  1068. ),
  1069. },
  1070. ),
  1071. (
  1072. [
  1073. BusinessHour(-1),
  1074. BusinessHour(n=-2),
  1075. BusinessHour(n=-4),
  1076. BusinessHour(n=-1, end="10:00"),
  1077. BusinessHour(n=-2, end="4:00"),
  1078. BusinessHour(n=-4, end="15:00"),
  1079. ],
  1080. {
  1081. datetime(2014, 7, 1, 11): (
  1082. datetime(2014, 7, 1, 9),
  1083. datetime(2014, 7, 2, 9),
  1084. ),
  1085. datetime(2014, 7, 1, 18): (
  1086. datetime(2014, 7, 1, 9),
  1087. datetime(2014, 7, 2, 9),
  1088. ),
  1089. datetime(2014, 7, 1, 23): (
  1090. datetime(2014, 7, 1, 9),
  1091. datetime(2014, 7, 2, 9),
  1092. ),
  1093. datetime(2014, 7, 2, 8): (
  1094. datetime(2014, 7, 1, 9),
  1095. datetime(2014, 7, 2, 9),
  1096. ),
  1097. datetime(2014, 7, 2, 9): (
  1098. datetime(2014, 7, 2, 9),
  1099. datetime(2014, 7, 2, 9),
  1100. ),
  1101. datetime(2014, 7, 2, 10): (
  1102. datetime(2014, 7, 2, 9),
  1103. datetime(2014, 7, 3, 9),
  1104. ),
  1105. datetime(2014, 7, 5, 10): (
  1106. datetime(2014, 7, 4, 9),
  1107. datetime(2014, 7, 7, 9),
  1108. ),
  1109. datetime(2014, 7, 4, 10): (
  1110. datetime(2014, 7, 4, 9),
  1111. datetime(2014, 7, 7, 9),
  1112. ),
  1113. datetime(2014, 7, 4, 23): (
  1114. datetime(2014, 7, 4, 9),
  1115. datetime(2014, 7, 7, 9),
  1116. ),
  1117. datetime(2014, 7, 6, 10): (
  1118. datetime(2014, 7, 4, 9),
  1119. datetime(2014, 7, 7, 9),
  1120. ),
  1121. datetime(2014, 7, 7, 5): (
  1122. datetime(2014, 7, 4, 9),
  1123. datetime(2014, 7, 7, 9),
  1124. ),
  1125. datetime(2014, 7, 7, 9): (
  1126. datetime(2014, 7, 7, 9),
  1127. datetime(2014, 7, 7, 9),
  1128. ),
  1129. datetime(2014, 7, 7, 9, 1): (
  1130. datetime(2014, 7, 7, 9),
  1131. datetime(2014, 7, 8, 9),
  1132. ),
  1133. },
  1134. ),
  1135. (
  1136. [
  1137. BusinessHour(start="17:00", end="05:00"),
  1138. BusinessHour(n=3, start="17:00", end="03:00"),
  1139. ],
  1140. {
  1141. datetime(2014, 7, 1, 11): (
  1142. datetime(2014, 7, 1, 17),
  1143. datetime(2014, 6, 30, 17),
  1144. ),
  1145. datetime(2014, 7, 1, 18): (
  1146. datetime(2014, 7, 2, 17),
  1147. datetime(2014, 7, 1, 17),
  1148. ),
  1149. datetime(2014, 7, 1, 23): (
  1150. datetime(2014, 7, 2, 17),
  1151. datetime(2014, 7, 1, 17),
  1152. ),
  1153. datetime(2014, 7, 2, 8): (
  1154. datetime(2014, 7, 2, 17),
  1155. datetime(2014, 7, 1, 17),
  1156. ),
  1157. datetime(2014, 7, 2, 9): (
  1158. datetime(2014, 7, 2, 17),
  1159. datetime(2014, 7, 1, 17),
  1160. ),
  1161. datetime(2014, 7, 4, 17): (
  1162. datetime(2014, 7, 4, 17),
  1163. datetime(2014, 7, 4, 17),
  1164. ),
  1165. datetime(2014, 7, 5, 10): (
  1166. datetime(2014, 7, 7, 17),
  1167. datetime(2014, 7, 4, 17),
  1168. ),
  1169. datetime(2014, 7, 4, 10): (
  1170. datetime(2014, 7, 4, 17),
  1171. datetime(2014, 7, 3, 17),
  1172. ),
  1173. datetime(2014, 7, 4, 23): (
  1174. datetime(2014, 7, 7, 17),
  1175. datetime(2014, 7, 4, 17),
  1176. ),
  1177. datetime(2014, 7, 6, 10): (
  1178. datetime(2014, 7, 7, 17),
  1179. datetime(2014, 7, 4, 17),
  1180. ),
  1181. datetime(2014, 7, 7, 5): (
  1182. datetime(2014, 7, 7, 17),
  1183. datetime(2014, 7, 4, 17),
  1184. ),
  1185. datetime(2014, 7, 7, 17, 1): (
  1186. datetime(2014, 7, 8, 17),
  1187. datetime(2014, 7, 7, 17),
  1188. ),
  1189. },
  1190. ),
  1191. (
  1192. [
  1193. BusinessHour(-1, start="17:00", end="05:00"),
  1194. BusinessHour(n=-2, start="17:00", end="03:00"),
  1195. ],
  1196. {
  1197. datetime(2014, 7, 1, 11): (
  1198. datetime(2014, 6, 30, 17),
  1199. datetime(2014, 7, 1, 17),
  1200. ),
  1201. datetime(2014, 7, 1, 18): (
  1202. datetime(2014, 7, 1, 17),
  1203. datetime(2014, 7, 2, 17),
  1204. ),
  1205. datetime(2014, 7, 1, 23): (
  1206. datetime(2014, 7, 1, 17),
  1207. datetime(2014, 7, 2, 17),
  1208. ),
  1209. datetime(2014, 7, 2, 8): (
  1210. datetime(2014, 7, 1, 17),
  1211. datetime(2014, 7, 2, 17),
  1212. ),
  1213. datetime(2014, 7, 2, 9): (
  1214. datetime(2014, 7, 1, 17),
  1215. datetime(2014, 7, 2, 17),
  1216. ),
  1217. datetime(2014, 7, 2, 16, 59): (
  1218. datetime(2014, 7, 1, 17),
  1219. datetime(2014, 7, 2, 17),
  1220. ),
  1221. datetime(2014, 7, 5, 10): (
  1222. datetime(2014, 7, 4, 17),
  1223. datetime(2014, 7, 7, 17),
  1224. ),
  1225. datetime(2014, 7, 4, 10): (
  1226. datetime(2014, 7, 3, 17),
  1227. datetime(2014, 7, 4, 17),
  1228. ),
  1229. datetime(2014, 7, 4, 23): (
  1230. datetime(2014, 7, 4, 17),
  1231. datetime(2014, 7, 7, 17),
  1232. ),
  1233. datetime(2014, 7, 6, 10): (
  1234. datetime(2014, 7, 4, 17),
  1235. datetime(2014, 7, 7, 17),
  1236. ),
  1237. datetime(2014, 7, 7, 5): (
  1238. datetime(2014, 7, 4, 17),
  1239. datetime(2014, 7, 7, 17),
  1240. ),
  1241. datetime(2014, 7, 7, 18): (
  1242. datetime(2014, 7, 7, 17),
  1243. datetime(2014, 7, 8, 17),
  1244. ),
  1245. },
  1246. ),
  1247. (
  1248. [
  1249. BusinessHour(start=["11:15", "15:00"], end=["13:00", "20:00"]),
  1250. BusinessHour(n=3, start=["11:15", "15:00"], end=["12:00", "20:00"]),
  1251. BusinessHour(start=["11:15", "15:00"], end=["13:00", "17:00"]),
  1252. BusinessHour(n=2, start=["11:15", "15:00"], end=["12:00", "03:00"]),
  1253. BusinessHour(n=3, start=["11:15", "15:00"], end=["13:00", "16:00"]),
  1254. ],
  1255. {
  1256. datetime(2014, 7, 1, 11): (
  1257. datetime(2014, 7, 1, 11, 15),
  1258. datetime(2014, 6, 30, 15),
  1259. ),
  1260. datetime(2014, 7, 1, 18): (
  1261. datetime(2014, 7, 2, 11, 15),
  1262. datetime(2014, 7, 1, 15),
  1263. ),
  1264. datetime(2014, 7, 1, 23): (
  1265. datetime(2014, 7, 2, 11, 15),
  1266. datetime(2014, 7, 1, 15),
  1267. ),
  1268. datetime(2014, 7, 2, 8): (
  1269. datetime(2014, 7, 2, 11, 15),
  1270. datetime(2014, 7, 1, 15),
  1271. ),
  1272. datetime(2014, 7, 2, 9): (
  1273. datetime(2014, 7, 2, 11, 15),
  1274. datetime(2014, 7, 1, 15),
  1275. ),
  1276. datetime(2014, 7, 2, 10): (
  1277. datetime(2014, 7, 2, 11, 15),
  1278. datetime(2014, 7, 1, 15),
  1279. ),
  1280. datetime(2014, 7, 2, 11, 15): (
  1281. datetime(2014, 7, 2, 11, 15),
  1282. datetime(2014, 7, 2, 11, 15),
  1283. ),
  1284. datetime(2014, 7, 2, 11, 15, 1): (
  1285. datetime(2014, 7, 2, 15),
  1286. datetime(2014, 7, 2, 11, 15),
  1287. ),
  1288. datetime(2014, 7, 5, 10): (
  1289. datetime(2014, 7, 7, 11, 15),
  1290. datetime(2014, 7, 4, 15),
  1291. ),
  1292. datetime(2014, 7, 4, 10): (
  1293. datetime(2014, 7, 4, 11, 15),
  1294. datetime(2014, 7, 3, 15),
  1295. ),
  1296. datetime(2014, 7, 4, 23): (
  1297. datetime(2014, 7, 7, 11, 15),
  1298. datetime(2014, 7, 4, 15),
  1299. ),
  1300. datetime(2014, 7, 6, 10): (
  1301. datetime(2014, 7, 7, 11, 15),
  1302. datetime(2014, 7, 4, 15),
  1303. ),
  1304. datetime(2014, 7, 7, 5): (
  1305. datetime(2014, 7, 7, 11, 15),
  1306. datetime(2014, 7, 4, 15),
  1307. ),
  1308. datetime(2014, 7, 7, 9, 1): (
  1309. datetime(2014, 7, 7, 11, 15),
  1310. datetime(2014, 7, 4, 15),
  1311. ),
  1312. datetime(2014, 7, 7, 12): (
  1313. datetime(2014, 7, 7, 15),
  1314. datetime(2014, 7, 7, 11, 15),
  1315. ),
  1316. },
  1317. ),
  1318. (
  1319. [
  1320. BusinessHour(n=-1, start=["17:00", "08:00"], end=["05:00", "10:00"]),
  1321. BusinessHour(n=-2, start=["08:00", "17:00"], end=["10:00", "03:00"]),
  1322. ],
  1323. {
  1324. datetime(2014, 7, 1, 11): (
  1325. datetime(2014, 7, 1, 8),
  1326. datetime(2014, 7, 1, 17),
  1327. ),
  1328. datetime(2014, 7, 1, 18): (
  1329. datetime(2014, 7, 1, 17),
  1330. datetime(2014, 7, 2, 8),
  1331. ),
  1332. datetime(2014, 7, 1, 23): (
  1333. datetime(2014, 7, 1, 17),
  1334. datetime(2014, 7, 2, 8),
  1335. ),
  1336. datetime(2014, 7, 2, 8): (
  1337. datetime(2014, 7, 2, 8),
  1338. datetime(2014, 7, 2, 8),
  1339. ),
  1340. datetime(2014, 7, 2, 9): (
  1341. datetime(2014, 7, 2, 8),
  1342. datetime(2014, 7, 2, 17),
  1343. ),
  1344. datetime(2014, 7, 2, 16, 59): (
  1345. datetime(2014, 7, 2, 8),
  1346. datetime(2014, 7, 2, 17),
  1347. ),
  1348. datetime(2014, 7, 5, 10): (
  1349. datetime(2014, 7, 4, 17),
  1350. datetime(2014, 7, 7, 8),
  1351. ),
  1352. datetime(2014, 7, 4, 10): (
  1353. datetime(2014, 7, 4, 8),
  1354. datetime(2014, 7, 4, 17),
  1355. ),
  1356. datetime(2014, 7, 4, 23): (
  1357. datetime(2014, 7, 4, 17),
  1358. datetime(2014, 7, 7, 8),
  1359. ),
  1360. datetime(2014, 7, 6, 10): (
  1361. datetime(2014, 7, 4, 17),
  1362. datetime(2014, 7, 7, 8),
  1363. ),
  1364. datetime(2014, 7, 7, 5): (
  1365. datetime(2014, 7, 4, 17),
  1366. datetime(2014, 7, 7, 8),
  1367. ),
  1368. datetime(2014, 7, 7, 18): (
  1369. datetime(2014, 7, 7, 17),
  1370. datetime(2014, 7, 8, 8),
  1371. ),
  1372. },
  1373. ),
  1374. ]
  1375. @pytest.mark.parametrize("case", opening_time_cases)
  1376. def test_opening_time(self, case):
  1377. _offsets, cases = case
  1378. for offset in _offsets:
  1379. for dt, (exp_next, exp_prev) in cases.items():
  1380. assert offset._next_opening_time(dt) == exp_next
  1381. assert offset._prev_opening_time(dt) == exp_prev