test_scalar_compat.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. """
  2. Tests for DatetimeIndex methods behaving like their Timestamp counterparts
  3. """
  4. from datetime import datetime
  5. import numpy as np
  6. import pytest
  7. from pandas._libs.tslibs import (
  8. OutOfBoundsDatetime,
  9. to_offset,
  10. )
  11. from pandas._libs.tslibs.offsets import INVALID_FREQ_ERR_MSG
  12. import pandas as pd
  13. from pandas import (
  14. DatetimeIndex,
  15. Timestamp,
  16. date_range,
  17. )
  18. import pandas._testing as tm
  19. class TestDatetimeIndexOps:
  20. def test_dti_time(self):
  21. rng = date_range("1/1/2000", freq="12min", periods=10)
  22. result = pd.Index(rng).time
  23. expected = [t.time() for t in rng]
  24. assert (result == expected).all()
  25. def test_dti_date(self):
  26. rng = date_range("1/1/2000", freq="12H", periods=10)
  27. result = pd.Index(rng).date
  28. expected = [t.date() for t in rng]
  29. assert (result == expected).all()
  30. @pytest.mark.parametrize("data", [["1400-01-01"], [datetime(1400, 1, 1)]])
  31. def test_dti_date_out_of_range(self, data):
  32. # GH#1475
  33. msg = (
  34. "^Out of bounds nanosecond timestamp: "
  35. "1400-01-01( 00:00:00)?, at position 0$"
  36. )
  37. with pytest.raises(OutOfBoundsDatetime, match=msg):
  38. DatetimeIndex(data)
  39. @pytest.mark.parametrize(
  40. "field",
  41. [
  42. "dayofweek",
  43. "day_of_week",
  44. "dayofyear",
  45. "day_of_year",
  46. "quarter",
  47. "days_in_month",
  48. "is_month_start",
  49. "is_month_end",
  50. "is_quarter_start",
  51. "is_quarter_end",
  52. "is_year_start",
  53. "is_year_end",
  54. ],
  55. )
  56. def test_dti_timestamp_fields(self, field):
  57. # extra fields from DatetimeIndex like quarter and week
  58. idx = tm.makeDateIndex(100)
  59. expected = getattr(idx, field)[-1]
  60. result = getattr(Timestamp(idx[-1]), field)
  61. assert result == expected
  62. def test_dti_timestamp_isocalendar_fields(self):
  63. idx = tm.makeDateIndex(100)
  64. expected = tuple(idx.isocalendar().iloc[-1].to_list())
  65. result = idx[-1].isocalendar()
  66. assert result == expected
  67. # ----------------------------------------------------------------
  68. # DatetimeIndex.round
  69. def test_round_daily(self):
  70. dti = date_range("20130101 09:10:11", periods=5)
  71. result = dti.round("D")
  72. expected = date_range("20130101", periods=5)
  73. tm.assert_index_equal(result, expected)
  74. dti = dti.tz_localize("UTC").tz_convert("US/Eastern")
  75. result = dti.round("D")
  76. expected = date_range("20130101", periods=5).tz_localize("US/Eastern")
  77. tm.assert_index_equal(result, expected)
  78. result = dti.round("s")
  79. tm.assert_index_equal(result, dti)
  80. @pytest.mark.parametrize(
  81. "freq, error_msg",
  82. [
  83. ("Y", "<YearEnd: month=12> is a non-fixed frequency"),
  84. ("M", "<MonthEnd> is a non-fixed frequency"),
  85. ("foobar", "Invalid frequency: foobar"),
  86. ],
  87. )
  88. def test_round_invalid(self, freq, error_msg):
  89. dti = date_range("20130101 09:10:11", periods=5)
  90. dti = dti.tz_localize("UTC").tz_convert("US/Eastern")
  91. with pytest.raises(ValueError, match=error_msg):
  92. dti.round(freq)
  93. def test_round(self, tz_naive_fixture):
  94. tz = tz_naive_fixture
  95. rng = date_range(start="2016-01-01", periods=5, freq="30Min", tz=tz)
  96. elt = rng[1]
  97. expected_rng = DatetimeIndex(
  98. [
  99. Timestamp("2016-01-01 00:00:00", tz=tz),
  100. Timestamp("2016-01-01 00:00:00", tz=tz),
  101. Timestamp("2016-01-01 01:00:00", tz=tz),
  102. Timestamp("2016-01-01 02:00:00", tz=tz),
  103. Timestamp("2016-01-01 02:00:00", tz=tz),
  104. ]
  105. )
  106. expected_elt = expected_rng[1]
  107. tm.assert_index_equal(rng.round(freq="H"), expected_rng)
  108. assert elt.round(freq="H") == expected_elt
  109. msg = INVALID_FREQ_ERR_MSG
  110. with pytest.raises(ValueError, match=msg):
  111. rng.round(freq="foo")
  112. with pytest.raises(ValueError, match=msg):
  113. elt.round(freq="foo")
  114. msg = "<MonthEnd> is a non-fixed frequency"
  115. with pytest.raises(ValueError, match=msg):
  116. rng.round(freq="M")
  117. with pytest.raises(ValueError, match=msg):
  118. elt.round(freq="M")
  119. # GH#14440 & GH#15578
  120. index = DatetimeIndex(["2016-10-17 12:00:00.0015"], tz=tz)
  121. result = index.round("ms")
  122. expected = DatetimeIndex(["2016-10-17 12:00:00.002000"], tz=tz)
  123. tm.assert_index_equal(result, expected)
  124. for freq in ["us", "ns"]:
  125. tm.assert_index_equal(index, index.round(freq))
  126. index = DatetimeIndex(["2016-10-17 12:00:00.00149"], tz=tz)
  127. result = index.round("ms")
  128. expected = DatetimeIndex(["2016-10-17 12:00:00.001000"], tz=tz)
  129. tm.assert_index_equal(result, expected)
  130. index = DatetimeIndex(["2016-10-17 12:00:00.001501031"])
  131. result = index.round("10ns")
  132. expected = DatetimeIndex(["2016-10-17 12:00:00.001501030"])
  133. tm.assert_index_equal(result, expected)
  134. with tm.assert_produces_warning(False):
  135. ts = "2016-10-17 12:00:00.001501031"
  136. DatetimeIndex([ts]).round("1010ns")
  137. def test_no_rounding_occurs(self, tz_naive_fixture):
  138. # GH 21262
  139. tz = tz_naive_fixture
  140. rng = date_range(start="2016-01-01", periods=5, freq="2Min", tz=tz)
  141. expected_rng = DatetimeIndex(
  142. [
  143. Timestamp("2016-01-01 00:00:00", tz=tz),
  144. Timestamp("2016-01-01 00:02:00", tz=tz),
  145. Timestamp("2016-01-01 00:04:00", tz=tz),
  146. Timestamp("2016-01-01 00:06:00", tz=tz),
  147. Timestamp("2016-01-01 00:08:00", tz=tz),
  148. ]
  149. )
  150. tm.assert_index_equal(rng.round(freq="2T"), expected_rng)
  151. @pytest.mark.parametrize(
  152. "test_input, rounder, freq, expected",
  153. [
  154. (["2117-01-01 00:00:45"], "floor", "15s", ["2117-01-01 00:00:45"]),
  155. (["2117-01-01 00:00:45"], "ceil", "15s", ["2117-01-01 00:00:45"]),
  156. (
  157. ["2117-01-01 00:00:45.000000012"],
  158. "floor",
  159. "10ns",
  160. ["2117-01-01 00:00:45.000000010"],
  161. ),
  162. (
  163. ["1823-01-01 00:00:01.000000012"],
  164. "ceil",
  165. "10ns",
  166. ["1823-01-01 00:00:01.000000020"],
  167. ),
  168. (["1823-01-01 00:00:01"], "floor", "1s", ["1823-01-01 00:00:01"]),
  169. (["1823-01-01 00:00:01"], "ceil", "1s", ["1823-01-01 00:00:01"]),
  170. (["2018-01-01 00:15:00"], "ceil", "15T", ["2018-01-01 00:15:00"]),
  171. (["2018-01-01 00:15:00"], "floor", "15T", ["2018-01-01 00:15:00"]),
  172. (["1823-01-01 03:00:00"], "ceil", "3H", ["1823-01-01 03:00:00"]),
  173. (["1823-01-01 03:00:00"], "floor", "3H", ["1823-01-01 03:00:00"]),
  174. (
  175. ("NaT", "1823-01-01 00:00:01"),
  176. "floor",
  177. "1s",
  178. ("NaT", "1823-01-01 00:00:01"),
  179. ),
  180. (
  181. ("NaT", "1823-01-01 00:00:01"),
  182. "ceil",
  183. "1s",
  184. ("NaT", "1823-01-01 00:00:01"),
  185. ),
  186. ],
  187. )
  188. def test_ceil_floor_edge(self, test_input, rounder, freq, expected):
  189. dt = DatetimeIndex(list(test_input))
  190. func = getattr(dt, rounder)
  191. result = func(freq)
  192. expected = DatetimeIndex(list(expected))
  193. assert expected.equals(result)
  194. @pytest.mark.parametrize(
  195. "start, index_freq, periods",
  196. [("2018-01-01", "12H", 25), ("2018-01-01 0:0:0.124999", "1ns", 1000)],
  197. )
  198. @pytest.mark.parametrize(
  199. "round_freq",
  200. [
  201. "2ns",
  202. "3ns",
  203. "4ns",
  204. "5ns",
  205. "6ns",
  206. "7ns",
  207. "250ns",
  208. "500ns",
  209. "750ns",
  210. "1us",
  211. "19us",
  212. "250us",
  213. "500us",
  214. "750us",
  215. "1s",
  216. "2s",
  217. "3s",
  218. "12H",
  219. "1D",
  220. ],
  221. )
  222. def test_round_int64(self, start, index_freq, periods, round_freq):
  223. dt = date_range(start=start, freq=index_freq, periods=periods)
  224. unit = to_offset(round_freq).nanos
  225. # test floor
  226. result = dt.floor(round_freq)
  227. diff = dt.asi8 - result.asi8
  228. mod = result.asi8 % unit
  229. assert (mod == 0).all(), f"floor not a {round_freq} multiple"
  230. assert (0 <= diff).all() and (diff < unit).all(), "floor error"
  231. # test ceil
  232. result = dt.ceil(round_freq)
  233. diff = result.asi8 - dt.asi8
  234. mod = result.asi8 % unit
  235. assert (mod == 0).all(), f"ceil not a {round_freq} multiple"
  236. assert (0 <= diff).all() and (diff < unit).all(), "ceil error"
  237. # test round
  238. result = dt.round(round_freq)
  239. diff = abs(result.asi8 - dt.asi8)
  240. mod = result.asi8 % unit
  241. assert (mod == 0).all(), f"round not a {round_freq} multiple"
  242. assert (diff <= unit // 2).all(), "round error"
  243. if unit % 2 == 0:
  244. assert (
  245. result.asi8[diff == unit // 2] % 2 == 0
  246. ).all(), "round half to even error"
  247. # ----------------------------------------------------------------
  248. # DatetimeIndex.normalize
  249. def test_normalize(self):
  250. rng = date_range("1/1/2000 9:30", periods=10, freq="D")
  251. result = rng.normalize()
  252. expected = date_range("1/1/2000", periods=10, freq="D")
  253. tm.assert_index_equal(result, expected)
  254. arr_ns = np.array([1380585623454345752, 1380585612343234312]).astype(
  255. "datetime64[ns]"
  256. )
  257. rng_ns = DatetimeIndex(arr_ns)
  258. rng_ns_normalized = rng_ns.normalize()
  259. arr_ns = np.array([1380585600000000000, 1380585600000000000]).astype(
  260. "datetime64[ns]"
  261. )
  262. expected = DatetimeIndex(arr_ns)
  263. tm.assert_index_equal(rng_ns_normalized, expected)
  264. assert result.is_normalized
  265. assert not rng.is_normalized
  266. def test_normalize_nat(self):
  267. dti = DatetimeIndex([pd.NaT, Timestamp("2018-01-01 01:00:00")])
  268. result = dti.normalize()
  269. expected = DatetimeIndex([pd.NaT, Timestamp("2018-01-01")])
  270. tm.assert_index_equal(result, expected)
  271. class TestDateTimeIndexToJulianDate:
  272. def test_1700(self):
  273. dr = date_range(start=Timestamp("1710-10-01"), periods=5, freq="D")
  274. r1 = pd.Index([x.to_julian_date() for x in dr])
  275. r2 = dr.to_julian_date()
  276. assert isinstance(r2, pd.Index) and r2.dtype == np.float64
  277. tm.assert_index_equal(r1, r2)
  278. def test_2000(self):
  279. dr = date_range(start=Timestamp("2000-02-27"), periods=5, freq="D")
  280. r1 = pd.Index([x.to_julian_date() for x in dr])
  281. r2 = dr.to_julian_date()
  282. assert isinstance(r2, pd.Index) and r2.dtype == np.float64
  283. tm.assert_index_equal(r1, r2)
  284. def test_hour(self):
  285. dr = date_range(start=Timestamp("2000-02-27"), periods=5, freq="H")
  286. r1 = pd.Index([x.to_julian_date() for x in dr])
  287. r2 = dr.to_julian_date()
  288. assert isinstance(r2, pd.Index) and r2.dtype == np.float64
  289. tm.assert_index_equal(r1, r2)
  290. def test_minute(self):
  291. dr = date_range(start=Timestamp("2000-02-27"), periods=5, freq="T")
  292. r1 = pd.Index([x.to_julian_date() for x in dr])
  293. r2 = dr.to_julian_date()
  294. assert isinstance(r2, pd.Index) and r2.dtype == np.float64
  295. tm.assert_index_equal(r1, r2)
  296. def test_second(self):
  297. dr = date_range(start=Timestamp("2000-02-27"), periods=5, freq="S")
  298. r1 = pd.Index([x.to_julian_date() for x in dr])
  299. r2 = dr.to_julian_date()
  300. assert isinstance(r2, pd.Index) and r2.dtype == np.float64
  301. tm.assert_index_equal(r1, r2)