test_common.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. from datetime import datetime
  2. from dateutil.tz.tz import tzlocal
  3. import pytest
  4. from pandas._libs.tslibs import (
  5. OutOfBoundsDatetime,
  6. Timestamp,
  7. )
  8. from pandas.compat import (
  9. IS64,
  10. is_platform_windows,
  11. )
  12. from pandas.tseries.offsets import (
  13. FY5253,
  14. BDay,
  15. BMonthBegin,
  16. BMonthEnd,
  17. BQuarterBegin,
  18. BQuarterEnd,
  19. BusinessHour,
  20. BYearBegin,
  21. BYearEnd,
  22. CBMonthBegin,
  23. CBMonthEnd,
  24. CDay,
  25. CustomBusinessHour,
  26. DateOffset,
  27. FY5253Quarter,
  28. LastWeekOfMonth,
  29. MonthBegin,
  30. MonthEnd,
  31. QuarterEnd,
  32. SemiMonthBegin,
  33. SemiMonthEnd,
  34. Week,
  35. WeekOfMonth,
  36. YearBegin,
  37. YearEnd,
  38. )
  39. def _get_offset(klass, value=1, normalize=False):
  40. # create instance from offset class
  41. if klass is FY5253:
  42. klass = klass(
  43. n=value,
  44. startingMonth=1,
  45. weekday=1,
  46. variation="last",
  47. normalize=normalize,
  48. )
  49. elif klass is FY5253Quarter:
  50. klass = klass(
  51. n=value,
  52. startingMonth=1,
  53. weekday=1,
  54. qtr_with_extra_week=1,
  55. variation="last",
  56. normalize=normalize,
  57. )
  58. elif klass is LastWeekOfMonth:
  59. klass = klass(n=value, weekday=5, normalize=normalize)
  60. elif klass is WeekOfMonth:
  61. klass = klass(n=value, week=1, weekday=5, normalize=normalize)
  62. elif klass is Week:
  63. klass = klass(n=value, weekday=5, normalize=normalize)
  64. elif klass is DateOffset:
  65. klass = klass(days=value, normalize=normalize)
  66. else:
  67. klass = klass(value, normalize=normalize)
  68. return klass
  69. @pytest.fixture(
  70. params=[
  71. BDay,
  72. BusinessHour,
  73. BMonthEnd,
  74. BMonthBegin,
  75. BQuarterEnd,
  76. BQuarterBegin,
  77. BYearEnd,
  78. BYearBegin,
  79. CDay,
  80. CustomBusinessHour,
  81. CBMonthEnd,
  82. CBMonthBegin,
  83. MonthEnd,
  84. MonthBegin,
  85. SemiMonthBegin,
  86. SemiMonthEnd,
  87. QuarterEnd,
  88. LastWeekOfMonth,
  89. WeekOfMonth,
  90. Week,
  91. YearBegin,
  92. YearEnd,
  93. FY5253,
  94. FY5253Quarter,
  95. DateOffset,
  96. ]
  97. )
  98. def _offset(request):
  99. return request.param
  100. @pytest.fixture
  101. def dt(_offset):
  102. if _offset in (CBMonthBegin, CBMonthEnd, BDay):
  103. return Timestamp(2008, 1, 1)
  104. elif _offset is (CustomBusinessHour, BusinessHour):
  105. return Timestamp(2014, 7, 1, 10, 00)
  106. return Timestamp(2008, 1, 2)
  107. def test_apply_out_of_range(request, tz_naive_fixture, _offset):
  108. tz = tz_naive_fixture
  109. # try to create an out-of-bounds result timestamp; if we can't create
  110. # the offset skip
  111. try:
  112. if _offset in (BusinessHour, CustomBusinessHour):
  113. # Using 10000 in BusinessHour fails in tz check because of DST
  114. # difference
  115. offset = _get_offset(_offset, value=100000)
  116. else:
  117. offset = _get_offset(_offset, value=10000)
  118. result = Timestamp("20080101") + offset
  119. assert isinstance(result, datetime)
  120. assert result.tzinfo is None
  121. # Check tz is preserved
  122. t = Timestamp("20080101", tz=tz)
  123. result = t + offset
  124. assert isinstance(result, datetime)
  125. if tz is not None:
  126. assert t.tzinfo is not None
  127. if isinstance(tz, tzlocal) and not IS64 and _offset is not DateOffset:
  128. # If we hit OutOfBoundsDatetime on non-64 bit machines
  129. # we'll drop out of the try clause before the next test
  130. request.node.add_marker(
  131. pytest.mark.xfail(reason="OverflowError inside tzlocal past 2038")
  132. )
  133. elif (
  134. isinstance(tz, tzlocal)
  135. and is_platform_windows()
  136. and _offset in (QuarterEnd, BQuarterBegin, BQuarterEnd)
  137. ):
  138. request.node.add_marker(
  139. pytest.mark.xfail(reason="After GH#49737 t.tzinfo is None on CI")
  140. )
  141. assert str(t.tzinfo) == str(result.tzinfo)
  142. except OutOfBoundsDatetime:
  143. pass
  144. except (ValueError, KeyError):
  145. # we are creating an invalid offset
  146. # so ignore
  147. pass
  148. def test_offsets_compare_equal(_offset):
  149. # root cause of GH#456: __ne__ was not implemented
  150. offset1 = _offset()
  151. offset2 = _offset()
  152. assert not offset1 != offset2
  153. assert offset1 == offset2
  154. @pytest.mark.parametrize(
  155. "date, offset2",
  156. [
  157. [Timestamp(2008, 1, 1), BDay(2)],
  158. [Timestamp(2014, 7, 1, 10, 00), BusinessHour(n=3)],
  159. [
  160. Timestamp(2014, 7, 1, 10),
  161. CustomBusinessHour(
  162. holidays=["2014-06-27", Timestamp(2014, 6, 30), Timestamp("2014-07-02")]
  163. ),
  164. ],
  165. [Timestamp(2008, 1, 2), SemiMonthEnd(2)],
  166. [Timestamp(2008, 1, 2), SemiMonthBegin(2)],
  167. [Timestamp(2008, 1, 2), Week(2)],
  168. [Timestamp(2008, 1, 2), WeekOfMonth(2)],
  169. [Timestamp(2008, 1, 2), LastWeekOfMonth(2)],
  170. ],
  171. )
  172. def test_rsub(date, offset2):
  173. assert date - offset2 == (-offset2)._apply(date)
  174. @pytest.mark.parametrize(
  175. "date, offset2",
  176. [
  177. [Timestamp(2008, 1, 1), BDay(2)],
  178. [Timestamp(2014, 7, 1, 10, 00), BusinessHour(n=3)],
  179. [
  180. Timestamp(2014, 7, 1, 10),
  181. CustomBusinessHour(
  182. holidays=["2014-06-27", Timestamp(2014, 6, 30), Timestamp("2014-07-02")]
  183. ),
  184. ],
  185. [Timestamp(2008, 1, 2), SemiMonthEnd(2)],
  186. [Timestamp(2008, 1, 2), SemiMonthBegin(2)],
  187. [Timestamp(2008, 1, 2), Week(2)],
  188. [Timestamp(2008, 1, 2), WeekOfMonth(2)],
  189. [Timestamp(2008, 1, 2), LastWeekOfMonth(2)],
  190. ],
  191. )
  192. def test_radd(date, offset2):
  193. assert date + offset2 == offset2 + date
  194. @pytest.mark.parametrize(
  195. "date, offset_box, offset2",
  196. [
  197. [Timestamp(2008, 1, 1), BDay, BDay(2)],
  198. [Timestamp(2008, 1, 2), SemiMonthEnd, SemiMonthEnd(2)],
  199. [Timestamp(2008, 1, 2), SemiMonthBegin, SemiMonthBegin(2)],
  200. [Timestamp(2008, 1, 2), Week, Week(2)],
  201. [Timestamp(2008, 1, 2), WeekOfMonth, WeekOfMonth(2)],
  202. [Timestamp(2008, 1, 2), LastWeekOfMonth, LastWeekOfMonth(2)],
  203. ],
  204. )
  205. def test_sub(date, offset_box, offset2):
  206. off = offset2
  207. msg = "Cannot subtract datetime from offset"
  208. with pytest.raises(TypeError, match=msg):
  209. off - date
  210. assert 2 * off - off == off
  211. assert date - offset2 == date + offset_box(-2)
  212. assert date - offset2 == date - (2 * off - off)
  213. @pytest.mark.parametrize(
  214. "offset_box, offset1",
  215. [
  216. [BDay, BDay()],
  217. [LastWeekOfMonth, LastWeekOfMonth()],
  218. [WeekOfMonth, WeekOfMonth()],
  219. [Week, Week()],
  220. [SemiMonthBegin, SemiMonthBegin()],
  221. [SemiMonthEnd, SemiMonthEnd()],
  222. [CustomBusinessHour, CustomBusinessHour(weekmask="Tue Wed Thu Fri")],
  223. [BusinessHour, BusinessHour()],
  224. ],
  225. )
  226. def test_Mult1(offset_box, offset1, dt):
  227. assert dt + 10 * offset1 == dt + offset_box(10)
  228. assert dt + 5 * offset1 == dt + offset_box(5)
  229. def test_compare_str(_offset):
  230. # GH#23524
  231. # comparing to strings that cannot be cast to DateOffsets should
  232. # not raise for __eq__ or __ne__
  233. off = _get_offset(_offset)
  234. assert not off == "infer"
  235. assert off != "foo"
  236. # Note: inequalities are only implemented for Tick subclasses;
  237. # tests for this are in test_ticks