test_custom_business_month.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. """
  2. Tests for the following offsets:
  3. - CustomBusinessMonthBase
  4. - CustomBusinessMonthBegin
  5. - CustomBusinessMonthEnd
  6. """
  7. from __future__ import annotations
  8. from datetime import (
  9. date,
  10. datetime,
  11. timedelta,
  12. )
  13. import numpy as np
  14. import pytest
  15. from pandas._libs.tslibs.offsets import (
  16. CBMonthBegin,
  17. CBMonthEnd,
  18. CDay,
  19. )
  20. from pandas import (
  21. _testing as tm,
  22. date_range,
  23. )
  24. from pandas.tests.tseries.offsets.common import (
  25. assert_is_on_offset,
  26. assert_offset_equal,
  27. )
  28. from pandas.tests.tseries.offsets.test_offsets import _ApplyCases
  29. from pandas.tseries import offsets
  30. from pandas.tseries.holiday import USFederalHolidayCalendar
  31. @pytest.fixture
  32. def dt():
  33. return datetime(2008, 1, 1)
  34. class TestCommonCBM:
  35. @pytest.mark.parametrize("offset2", [CBMonthBegin(2), CBMonthEnd(2)])
  36. def test_eq(self, offset2):
  37. assert offset2 == offset2
  38. @pytest.mark.parametrize("offset2", [CBMonthBegin(2), CBMonthEnd(2)])
  39. def test_hash(self, offset2):
  40. assert hash(offset2) == hash(offset2)
  41. @pytest.mark.parametrize("_offset", [CBMonthBegin, CBMonthEnd])
  42. def test_roundtrip_pickle(self, _offset):
  43. def _check_roundtrip(obj):
  44. unpickled = tm.round_trip_pickle(obj)
  45. assert unpickled == obj
  46. _check_roundtrip(_offset())
  47. _check_roundtrip(_offset(2))
  48. _check_roundtrip(_offset() * 2)
  49. @pytest.mark.parametrize("_offset", [CBMonthBegin, CBMonthEnd])
  50. def test_copy(self, _offset):
  51. # GH 17452
  52. off = _offset(weekmask="Mon Wed Fri")
  53. assert off == off.copy()
  54. class TestCustomBusinessMonthBegin:
  55. @pytest.fixture
  56. def _offset(self):
  57. return CBMonthBegin
  58. @pytest.fixture
  59. def offset(self):
  60. return CBMonthBegin()
  61. @pytest.fixture
  62. def offset2(self):
  63. return CBMonthBegin(2)
  64. def test_different_normalize_equals(self, _offset):
  65. # GH#21404 changed __eq__ to return False when `normalize` does not match
  66. offset = _offset()
  67. offset2 = _offset(normalize=True)
  68. assert offset != offset2
  69. def test_repr(self, offset, offset2):
  70. assert repr(offset) == "<CustomBusinessMonthBegin>"
  71. assert repr(offset2) == "<2 * CustomBusinessMonthBegins>"
  72. def test_add_datetime(self, dt, offset2):
  73. assert offset2 + dt == datetime(2008, 3, 3)
  74. def testRollback1(self):
  75. assert CDay(10).rollback(datetime(2007, 12, 31)) == datetime(2007, 12, 31)
  76. def testRollback2(self, dt):
  77. assert CBMonthBegin(10).rollback(dt) == datetime(2008, 1, 1)
  78. def testRollforward1(self, dt):
  79. assert CBMonthBegin(10).rollforward(dt) == datetime(2008, 1, 1)
  80. def test_roll_date_object(self):
  81. offset = CBMonthBegin()
  82. dt = date(2012, 9, 15)
  83. result = offset.rollback(dt)
  84. assert result == datetime(2012, 9, 3)
  85. result = offset.rollforward(dt)
  86. assert result == datetime(2012, 10, 1)
  87. offset = offsets.Day()
  88. result = offset.rollback(dt)
  89. assert result == datetime(2012, 9, 15)
  90. result = offset.rollforward(dt)
  91. assert result == datetime(2012, 9, 15)
  92. on_offset_cases = [
  93. (CBMonthBegin(), datetime(2008, 1, 1), True),
  94. (CBMonthBegin(), datetime(2008, 1, 31), False),
  95. ]
  96. @pytest.mark.parametrize("case", on_offset_cases)
  97. def test_is_on_offset(self, case):
  98. offset, dt, expected = case
  99. assert_is_on_offset(offset, dt, expected)
  100. apply_cases: _ApplyCases = [
  101. (
  102. CBMonthBegin(),
  103. {
  104. datetime(2008, 1, 1): datetime(2008, 2, 1),
  105. datetime(2008, 2, 7): datetime(2008, 3, 3),
  106. },
  107. ),
  108. (
  109. 2 * CBMonthBegin(),
  110. {
  111. datetime(2008, 1, 1): datetime(2008, 3, 3),
  112. datetime(2008, 2, 7): datetime(2008, 4, 1),
  113. },
  114. ),
  115. (
  116. -CBMonthBegin(),
  117. {
  118. datetime(2008, 1, 1): datetime(2007, 12, 3),
  119. datetime(2008, 2, 8): datetime(2008, 2, 1),
  120. },
  121. ),
  122. (
  123. -2 * CBMonthBegin(),
  124. {
  125. datetime(2008, 1, 1): datetime(2007, 11, 1),
  126. datetime(2008, 2, 9): datetime(2008, 1, 1),
  127. },
  128. ),
  129. (
  130. CBMonthBegin(0),
  131. {
  132. datetime(2008, 1, 1): datetime(2008, 1, 1),
  133. datetime(2008, 1, 7): datetime(2008, 2, 1),
  134. },
  135. ),
  136. ]
  137. @pytest.mark.parametrize("case", apply_cases)
  138. def test_apply(self, case):
  139. offset, cases = case
  140. for base, expected in cases.items():
  141. assert_offset_equal(offset, base, expected)
  142. def test_apply_large_n(self):
  143. dt = datetime(2012, 10, 23)
  144. result = dt + CBMonthBegin(10)
  145. assert result == datetime(2013, 8, 1)
  146. result = dt + CDay(100) - CDay(100)
  147. assert result == dt
  148. off = CBMonthBegin() * 6
  149. rs = datetime(2012, 1, 1) - off
  150. xp = datetime(2011, 7, 1)
  151. assert rs == xp
  152. st = datetime(2011, 12, 18)
  153. rs = st + off
  154. xp = datetime(2012, 6, 1)
  155. assert rs == xp
  156. def test_holidays(self):
  157. # Define a TradingDay offset
  158. holidays = ["2012-02-01", datetime(2012, 2, 2), np.datetime64("2012-03-01")]
  159. bm_offset = CBMonthBegin(holidays=holidays)
  160. dt = datetime(2012, 1, 1)
  161. assert dt + bm_offset == datetime(2012, 1, 2)
  162. assert dt + 2 * bm_offset == datetime(2012, 2, 3)
  163. @pytest.mark.filterwarnings("ignore:Non:pandas.errors.PerformanceWarning")
  164. def test_datetimeindex(self):
  165. hcal = USFederalHolidayCalendar()
  166. cbmb = CBMonthBegin(calendar=hcal)
  167. assert date_range(start="20120101", end="20130101", freq=cbmb).tolist()[
  168. 0
  169. ] == datetime(2012, 1, 3)
  170. @pytest.mark.parametrize(
  171. "case",
  172. [
  173. (
  174. CBMonthBegin(n=1, offset=timedelta(days=5)),
  175. {
  176. datetime(2021, 3, 1): datetime(2021, 4, 1) + timedelta(days=5),
  177. datetime(2021, 4, 17): datetime(2021, 5, 3) + timedelta(days=5),
  178. },
  179. ),
  180. (
  181. CBMonthBegin(n=2, offset=timedelta(days=40)),
  182. {
  183. datetime(2021, 3, 10): datetime(2021, 5, 3) + timedelta(days=40),
  184. datetime(2021, 4, 30): datetime(2021, 6, 1) + timedelta(days=40),
  185. },
  186. ),
  187. (
  188. CBMonthBegin(n=1, offset=timedelta(days=-5)),
  189. {
  190. datetime(2021, 3, 1): datetime(2021, 4, 1) - timedelta(days=5),
  191. datetime(2021, 4, 11): datetime(2021, 5, 3) - timedelta(days=5),
  192. },
  193. ),
  194. (
  195. -2 * CBMonthBegin(n=1, offset=timedelta(days=10)),
  196. {
  197. datetime(2021, 3, 1): datetime(2021, 1, 1) + timedelta(days=10),
  198. datetime(2021, 4, 3): datetime(2021, 3, 1) + timedelta(days=10),
  199. },
  200. ),
  201. (
  202. CBMonthBegin(n=0, offset=timedelta(days=1)),
  203. {
  204. datetime(2021, 3, 2): datetime(2021, 4, 1) + timedelta(days=1),
  205. datetime(2021, 4, 1): datetime(2021, 4, 1) + timedelta(days=1),
  206. },
  207. ),
  208. (
  209. CBMonthBegin(
  210. n=1, holidays=["2021-04-01", "2021-04-02"], offset=timedelta(days=1)
  211. ),
  212. {
  213. datetime(2021, 3, 2): datetime(2021, 4, 5) + timedelta(days=1),
  214. },
  215. ),
  216. ],
  217. )
  218. def test_apply_with_extra_offset(self, case):
  219. offset, cases = case
  220. for base, expected in cases.items():
  221. assert_offset_equal(offset, base, expected)
  222. class TestCustomBusinessMonthEnd:
  223. @pytest.fixture
  224. def _offset(self):
  225. return CBMonthEnd
  226. @pytest.fixture
  227. def offset(self):
  228. return CBMonthEnd()
  229. @pytest.fixture
  230. def offset2(self):
  231. return CBMonthEnd(2)
  232. def test_different_normalize_equals(self, _offset):
  233. # GH#21404 changed __eq__ to return False when `normalize` does not match
  234. offset = _offset()
  235. offset2 = _offset(normalize=True)
  236. assert offset != offset2
  237. def test_repr(self, offset, offset2):
  238. assert repr(offset) == "<CustomBusinessMonthEnd>"
  239. assert repr(offset2) == "<2 * CustomBusinessMonthEnds>"
  240. def test_add_datetime(self, dt, offset2):
  241. assert offset2 + dt == datetime(2008, 2, 29)
  242. def testRollback1(self):
  243. assert CDay(10).rollback(datetime(2007, 12, 31)) == datetime(2007, 12, 31)
  244. def testRollback2(self, dt):
  245. assert CBMonthEnd(10).rollback(dt) == datetime(2007, 12, 31)
  246. def testRollforward1(self, dt):
  247. assert CBMonthEnd(10).rollforward(dt) == datetime(2008, 1, 31)
  248. def test_roll_date_object(self):
  249. offset = CBMonthEnd()
  250. dt = date(2012, 9, 15)
  251. result = offset.rollback(dt)
  252. assert result == datetime(2012, 8, 31)
  253. result = offset.rollforward(dt)
  254. assert result == datetime(2012, 9, 28)
  255. offset = offsets.Day()
  256. result = offset.rollback(dt)
  257. assert result == datetime(2012, 9, 15)
  258. result = offset.rollforward(dt)
  259. assert result == datetime(2012, 9, 15)
  260. on_offset_cases = [
  261. (CBMonthEnd(), datetime(2008, 1, 31), True),
  262. (CBMonthEnd(), datetime(2008, 1, 1), False),
  263. ]
  264. @pytest.mark.parametrize("case", on_offset_cases)
  265. def test_is_on_offset(self, case):
  266. offset, dt, expected = case
  267. assert_is_on_offset(offset, dt, expected)
  268. apply_cases: _ApplyCases = [
  269. (
  270. CBMonthEnd(),
  271. {
  272. datetime(2008, 1, 1): datetime(2008, 1, 31),
  273. datetime(2008, 2, 7): datetime(2008, 2, 29),
  274. },
  275. ),
  276. (
  277. 2 * CBMonthEnd(),
  278. {
  279. datetime(2008, 1, 1): datetime(2008, 2, 29),
  280. datetime(2008, 2, 7): datetime(2008, 3, 31),
  281. },
  282. ),
  283. (
  284. -CBMonthEnd(),
  285. {
  286. datetime(2008, 1, 1): datetime(2007, 12, 31),
  287. datetime(2008, 2, 8): datetime(2008, 1, 31),
  288. },
  289. ),
  290. (
  291. -2 * CBMonthEnd(),
  292. {
  293. datetime(2008, 1, 1): datetime(2007, 11, 30),
  294. datetime(2008, 2, 9): datetime(2007, 12, 31),
  295. },
  296. ),
  297. (
  298. CBMonthEnd(0),
  299. {
  300. datetime(2008, 1, 1): datetime(2008, 1, 31),
  301. datetime(2008, 2, 7): datetime(2008, 2, 29),
  302. },
  303. ),
  304. ]
  305. @pytest.mark.parametrize("case", apply_cases)
  306. def test_apply(self, case):
  307. offset, cases = case
  308. for base, expected in cases.items():
  309. assert_offset_equal(offset, base, expected)
  310. def test_apply_large_n(self):
  311. dt = datetime(2012, 10, 23)
  312. result = dt + CBMonthEnd(10)
  313. assert result == datetime(2013, 7, 31)
  314. result = dt + CDay(100) - CDay(100)
  315. assert result == dt
  316. off = CBMonthEnd() * 6
  317. rs = datetime(2012, 1, 1) - off
  318. xp = datetime(2011, 7, 29)
  319. assert rs == xp
  320. st = datetime(2011, 12, 18)
  321. rs = st + off
  322. xp = datetime(2012, 5, 31)
  323. assert rs == xp
  324. def test_holidays(self):
  325. # Define a TradingDay offset
  326. holidays = ["2012-01-31", datetime(2012, 2, 28), np.datetime64("2012-02-29")]
  327. bm_offset = CBMonthEnd(holidays=holidays)
  328. dt = datetime(2012, 1, 1)
  329. assert dt + bm_offset == datetime(2012, 1, 30)
  330. assert dt + 2 * bm_offset == datetime(2012, 2, 27)
  331. @pytest.mark.filterwarnings("ignore:Non:pandas.errors.PerformanceWarning")
  332. def test_datetimeindex(self):
  333. hcal = USFederalHolidayCalendar()
  334. freq = CBMonthEnd(calendar=hcal)
  335. assert date_range(start="20120101", end="20130101", freq=freq).tolist()[
  336. 0
  337. ] == datetime(2012, 1, 31)
  338. @pytest.mark.parametrize(
  339. "case",
  340. [
  341. (
  342. CBMonthEnd(n=1, offset=timedelta(days=5)),
  343. {
  344. datetime(2021, 3, 1): datetime(2021, 3, 31) + timedelta(days=5),
  345. datetime(2021, 4, 17): datetime(2021, 4, 30) + timedelta(days=5),
  346. },
  347. ),
  348. (
  349. CBMonthEnd(n=2, offset=timedelta(days=40)),
  350. {
  351. datetime(2021, 3, 10): datetime(2021, 4, 30) + timedelta(days=40),
  352. datetime(2021, 4, 30): datetime(2021, 6, 30) + timedelta(days=40),
  353. },
  354. ),
  355. (
  356. CBMonthEnd(n=1, offset=timedelta(days=-5)),
  357. {
  358. datetime(2021, 3, 1): datetime(2021, 3, 31) - timedelta(days=5),
  359. datetime(2021, 4, 11): datetime(2021, 4, 30) - timedelta(days=5),
  360. },
  361. ),
  362. (
  363. -2 * CBMonthEnd(n=1, offset=timedelta(days=10)),
  364. {
  365. datetime(2021, 3, 1): datetime(2021, 1, 29) + timedelta(days=10),
  366. datetime(2021, 4, 3): datetime(2021, 2, 26) + timedelta(days=10),
  367. },
  368. ),
  369. (
  370. CBMonthEnd(n=0, offset=timedelta(days=1)),
  371. {
  372. datetime(2021, 3, 2): datetime(2021, 3, 31) + timedelta(days=1),
  373. datetime(2021, 4, 1): datetime(2021, 4, 30) + timedelta(days=1),
  374. },
  375. ),
  376. (
  377. CBMonthEnd(n=1, holidays=["2021-03-31"], offset=timedelta(days=1)),
  378. {
  379. datetime(2021, 3, 2): datetime(2021, 3, 30) + timedelta(days=1),
  380. },
  381. ),
  382. ],
  383. )
  384. def test_apply_with_extra_offset(self, case):
  385. offset, cases = case
  386. for base, expected in cases.items():
  387. assert_offset_equal(offset, base, expected)