test_ticks.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. """
  2. Tests for offsets.Tick and subclasses
  3. """
  4. from datetime import (
  5. datetime,
  6. timedelta,
  7. )
  8. from hypothesis import (
  9. assume,
  10. example,
  11. given,
  12. )
  13. import numpy as np
  14. import pytest
  15. from pandas._libs.tslibs.offsets import delta_to_tick
  16. from pandas.errors import OutOfBoundsTimedelta
  17. from pandas import (
  18. Timedelta,
  19. Timestamp,
  20. )
  21. import pandas._testing as tm
  22. from pandas._testing._hypothesis import INT_NEG_999_TO_POS_999
  23. from pandas.tests.tseries.offsets.common import assert_offset_equal
  24. from pandas.tseries import offsets
  25. from pandas.tseries.offsets import (
  26. Hour,
  27. Micro,
  28. Milli,
  29. Minute,
  30. Nano,
  31. Second,
  32. )
  33. # ---------------------------------------------------------------------
  34. # Test Helpers
  35. tick_classes = [Hour, Minute, Second, Milli, Micro, Nano]
  36. # ---------------------------------------------------------------------
  37. def test_apply_ticks():
  38. result = offsets.Hour(3) + offsets.Hour(4)
  39. exp = offsets.Hour(7)
  40. assert result == exp
  41. def test_delta_to_tick():
  42. delta = timedelta(3)
  43. tick = delta_to_tick(delta)
  44. assert tick == offsets.Day(3)
  45. td = Timedelta(nanoseconds=5)
  46. tick = delta_to_tick(td)
  47. assert tick == Nano(5)
  48. @pytest.mark.parametrize("cls", tick_classes)
  49. @example(n=2, m=3)
  50. @example(n=800, m=300)
  51. @example(n=1000, m=5)
  52. @given(n=INT_NEG_999_TO_POS_999, m=INT_NEG_999_TO_POS_999)
  53. def test_tick_add_sub(cls, n, m):
  54. # For all Tick subclasses and all integers n, m, we should have
  55. # tick(n) + tick(m) == tick(n+m)
  56. # tick(n) - tick(m) == tick(n-m)
  57. left = cls(n)
  58. right = cls(m)
  59. expected = cls(n + m)
  60. assert left + right == expected
  61. expected = cls(n - m)
  62. assert left - right == expected
  63. @pytest.mark.arm_slow
  64. @pytest.mark.parametrize("cls", tick_classes)
  65. @example(n=2, m=3)
  66. @given(n=INT_NEG_999_TO_POS_999, m=INT_NEG_999_TO_POS_999)
  67. def test_tick_equality(cls, n, m):
  68. assume(m != n)
  69. # tick == tock iff tick.n == tock.n
  70. left = cls(n)
  71. right = cls(m)
  72. assert left != right
  73. right = cls(n)
  74. assert left == right
  75. assert not left != right
  76. if n != 0:
  77. assert cls(n) != cls(-n)
  78. # ---------------------------------------------------------------------
  79. def test_Hour():
  80. assert_offset_equal(Hour(), datetime(2010, 1, 1), datetime(2010, 1, 1, 1))
  81. assert_offset_equal(Hour(-1), datetime(2010, 1, 1, 1), datetime(2010, 1, 1))
  82. assert_offset_equal(2 * Hour(), datetime(2010, 1, 1), datetime(2010, 1, 1, 2))
  83. assert_offset_equal(-1 * Hour(), datetime(2010, 1, 1, 1), datetime(2010, 1, 1))
  84. assert Hour(3) + Hour(2) == Hour(5)
  85. assert Hour(3) - Hour(2) == Hour()
  86. assert Hour(4) != Hour(1)
  87. def test_Minute():
  88. assert_offset_equal(Minute(), datetime(2010, 1, 1), datetime(2010, 1, 1, 0, 1))
  89. assert_offset_equal(Minute(-1), datetime(2010, 1, 1, 0, 1), datetime(2010, 1, 1))
  90. assert_offset_equal(2 * Minute(), datetime(2010, 1, 1), datetime(2010, 1, 1, 0, 2))
  91. assert_offset_equal(-1 * Minute(), datetime(2010, 1, 1, 0, 1), datetime(2010, 1, 1))
  92. assert Minute(3) + Minute(2) == Minute(5)
  93. assert Minute(3) - Minute(2) == Minute()
  94. assert Minute(5) != Minute()
  95. def test_Second():
  96. assert_offset_equal(Second(), datetime(2010, 1, 1), datetime(2010, 1, 1, 0, 0, 1))
  97. assert_offset_equal(Second(-1), datetime(2010, 1, 1, 0, 0, 1), datetime(2010, 1, 1))
  98. assert_offset_equal(
  99. 2 * Second(), datetime(2010, 1, 1), datetime(2010, 1, 1, 0, 0, 2)
  100. )
  101. assert_offset_equal(
  102. -1 * Second(), datetime(2010, 1, 1, 0, 0, 1), datetime(2010, 1, 1)
  103. )
  104. assert Second(3) + Second(2) == Second(5)
  105. assert Second(3) - Second(2) == Second()
  106. def test_Millisecond():
  107. assert_offset_equal(
  108. Milli(), datetime(2010, 1, 1), datetime(2010, 1, 1, 0, 0, 0, 1000)
  109. )
  110. assert_offset_equal(
  111. Milli(-1), datetime(2010, 1, 1, 0, 0, 0, 1000), datetime(2010, 1, 1)
  112. )
  113. assert_offset_equal(
  114. Milli(2), datetime(2010, 1, 1), datetime(2010, 1, 1, 0, 0, 0, 2000)
  115. )
  116. assert_offset_equal(
  117. 2 * Milli(), datetime(2010, 1, 1), datetime(2010, 1, 1, 0, 0, 0, 2000)
  118. )
  119. assert_offset_equal(
  120. -1 * Milli(), datetime(2010, 1, 1, 0, 0, 0, 1000), datetime(2010, 1, 1)
  121. )
  122. assert Milli(3) + Milli(2) == Milli(5)
  123. assert Milli(3) - Milli(2) == Milli()
  124. def test_MillisecondTimestampArithmetic():
  125. assert_offset_equal(
  126. Milli(), Timestamp("2010-01-01"), Timestamp("2010-01-01 00:00:00.001")
  127. )
  128. assert_offset_equal(
  129. Milli(-1), Timestamp("2010-01-01 00:00:00.001"), Timestamp("2010-01-01")
  130. )
  131. def test_Microsecond():
  132. assert_offset_equal(Micro(), datetime(2010, 1, 1), datetime(2010, 1, 1, 0, 0, 0, 1))
  133. assert_offset_equal(
  134. Micro(-1), datetime(2010, 1, 1, 0, 0, 0, 1), datetime(2010, 1, 1)
  135. )
  136. assert_offset_equal(
  137. 2 * Micro(), datetime(2010, 1, 1), datetime(2010, 1, 1, 0, 0, 0, 2)
  138. )
  139. assert_offset_equal(
  140. -1 * Micro(), datetime(2010, 1, 1, 0, 0, 0, 1), datetime(2010, 1, 1)
  141. )
  142. assert Micro(3) + Micro(2) == Micro(5)
  143. assert Micro(3) - Micro(2) == Micro()
  144. def test_NanosecondGeneric():
  145. timestamp = Timestamp(datetime(2010, 1, 1))
  146. assert timestamp.nanosecond == 0
  147. result = timestamp + Nano(10)
  148. assert result.nanosecond == 10
  149. reverse_result = Nano(10) + timestamp
  150. assert reverse_result.nanosecond == 10
  151. def test_Nanosecond():
  152. timestamp = Timestamp(datetime(2010, 1, 1))
  153. assert_offset_equal(Nano(), timestamp, timestamp + np.timedelta64(1, "ns"))
  154. assert_offset_equal(Nano(-1), timestamp + np.timedelta64(1, "ns"), timestamp)
  155. assert_offset_equal(2 * Nano(), timestamp, timestamp + np.timedelta64(2, "ns"))
  156. assert_offset_equal(-1 * Nano(), timestamp + np.timedelta64(1, "ns"), timestamp)
  157. assert Nano(3) + Nano(2) == Nano(5)
  158. assert Nano(3) - Nano(2) == Nano()
  159. # GH9284
  160. assert Nano(1) + Nano(10) == Nano(11)
  161. assert Nano(5) + Micro(1) == Nano(1005)
  162. assert Micro(5) + Nano(1) == Nano(5001)
  163. @pytest.mark.parametrize(
  164. "kls, expected",
  165. [
  166. (Hour, Timedelta(hours=5)),
  167. (Minute, Timedelta(hours=2, minutes=3)),
  168. (Second, Timedelta(hours=2, seconds=3)),
  169. (Milli, Timedelta(hours=2, milliseconds=3)),
  170. (Micro, Timedelta(hours=2, microseconds=3)),
  171. (Nano, Timedelta(hours=2, nanoseconds=3)),
  172. ],
  173. )
  174. def test_tick_addition(kls, expected):
  175. offset = kls(3)
  176. td = Timedelta(hours=2)
  177. for other in [td, td.to_pytimedelta(), td.to_timedelta64()]:
  178. result = offset + other
  179. assert isinstance(result, Timedelta)
  180. assert result == expected
  181. result = other + offset
  182. assert isinstance(result, Timedelta)
  183. assert result == expected
  184. def test_tick_delta_overflow():
  185. # GH#55503 raise OutOfBoundsTimedelta, not OverflowError
  186. tick = offsets.Day(10**9)
  187. msg = "Cannot cast 1000000000 days 00:00:00 to unit='ns' without overflow"
  188. depr_msg = "Day.delta is deprecated"
  189. with pytest.raises(OutOfBoundsTimedelta, match=msg):
  190. with tm.assert_produces_warning(FutureWarning, match=depr_msg):
  191. tick.delta
  192. @pytest.mark.parametrize("cls", tick_classes)
  193. def test_tick_division(cls):
  194. off = cls(10)
  195. assert off / cls(5) == 2
  196. assert off / 2 == cls(5)
  197. assert off / 2.0 == cls(5)
  198. assert off / off._as_pd_timedelta == 1
  199. assert off / off._as_pd_timedelta.to_timedelta64() == 1
  200. assert off / Nano(1) == off._as_pd_timedelta / Nano(1)._as_pd_timedelta
  201. if cls is not Nano:
  202. # A case where we end up with a smaller class
  203. result = off / 1000
  204. assert isinstance(result, offsets.Tick)
  205. assert not isinstance(result, cls)
  206. assert result._as_pd_timedelta == off._as_pd_timedelta / 1000
  207. if cls._nanos_inc < Timedelta(seconds=1)._value:
  208. # Case where we end up with a bigger class
  209. result = off / 0.001
  210. assert isinstance(result, offsets.Tick)
  211. assert not isinstance(result, cls)
  212. assert result._as_pd_timedelta == off._as_pd_timedelta / 0.001
  213. def test_tick_mul_float():
  214. off = Micro(2)
  215. # Case where we retain type
  216. result = off * 1.5
  217. expected = Micro(3)
  218. assert result == expected
  219. assert isinstance(result, Micro)
  220. # Case where we bump up to the next type
  221. result = off * 1.25
  222. expected = Nano(2500)
  223. assert result == expected
  224. assert isinstance(result, Nano)
  225. @pytest.mark.parametrize("cls", tick_classes)
  226. def test_tick_rdiv(cls):
  227. off = cls(10)
  228. delta = off._as_pd_timedelta
  229. td64 = delta.to_timedelta64()
  230. instance__type = ".".join([cls.__module__, cls.__name__])
  231. msg = (
  232. "unsupported operand type\\(s\\) for \\/: 'int'|'float' and "
  233. f"'{instance__type}'"
  234. )
  235. with pytest.raises(TypeError, match=msg):
  236. 2 / off
  237. with pytest.raises(TypeError, match=msg):
  238. 2.0 / off
  239. assert (td64 * 2.5) / off == 2.5
  240. if cls is not Nano:
  241. # skip pytimedelta for Nano since it gets dropped
  242. assert (delta.to_pytimedelta() * 2) / off == 2
  243. result = np.array([2 * td64, td64]) / off
  244. expected = np.array([2.0, 1.0])
  245. tm.assert_numpy_array_equal(result, expected)
  246. @pytest.mark.parametrize("cls1", tick_classes)
  247. @pytest.mark.parametrize("cls2", tick_classes)
  248. def test_tick_zero(cls1, cls2):
  249. assert cls1(0) == cls2(0)
  250. assert cls1(0) + cls2(0) == cls1(0)
  251. if cls1 is not Nano:
  252. assert cls1(2) + cls2(0) == cls1(2)
  253. if cls1 is Nano:
  254. assert cls1(2) + Nano(0) == cls1(2)
  255. @pytest.mark.parametrize("cls", tick_classes)
  256. def test_tick_equalities(cls):
  257. assert cls() == cls(1)
  258. @pytest.mark.parametrize("cls", tick_classes)
  259. def test_tick_offset(cls):
  260. msg = f"{cls.__name__}.is_anchored is deprecated "
  261. with tm.assert_produces_warning(FutureWarning, match=msg):
  262. assert not cls().is_anchored()
  263. @pytest.mark.parametrize("cls", tick_classes)
  264. def test_compare_ticks(cls):
  265. three = cls(3)
  266. four = cls(4)
  267. assert three < cls(4)
  268. assert cls(3) < four
  269. assert four > cls(3)
  270. assert cls(4) > three
  271. assert cls(3) == cls(3)
  272. assert cls(3) != cls(4)
  273. @pytest.mark.parametrize("cls", tick_classes)
  274. def test_compare_ticks_to_strs(cls):
  275. # GH#23524
  276. off = cls(19)
  277. # These tests should work with any strings, but we particularly are
  278. # interested in "infer" as that comparison is convenient to make in
  279. # Datetime/Timedelta Array/Index constructors
  280. assert not off == "infer"
  281. assert not "foo" == off
  282. instance_type = ".".join([cls.__module__, cls.__name__])
  283. msg = (
  284. "'<'|'<='|'>'|'>=' not supported between instances of "
  285. f"'str' and '{instance_type}'|'{instance_type}' and 'str'"
  286. )
  287. for left, right in [("infer", off), (off, "infer")]:
  288. with pytest.raises(TypeError, match=msg):
  289. left < right
  290. with pytest.raises(TypeError, match=msg):
  291. left <= right
  292. with pytest.raises(TypeError, match=msg):
  293. left > right
  294. with pytest.raises(TypeError, match=msg):
  295. left >= right
  296. @pytest.mark.parametrize("cls", tick_classes)
  297. def test_compare_ticks_to_timedeltalike(cls):
  298. off = cls(19)
  299. td = off._as_pd_timedelta
  300. others = [td, td.to_timedelta64()]
  301. if cls is not Nano:
  302. others.append(td.to_pytimedelta())
  303. for other in others:
  304. assert off == other
  305. assert not off != other
  306. assert not off < other
  307. assert not off > other
  308. assert off <= other
  309. assert off >= other