test_date_range.py 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258
  1. """
  2. test date_range, bdate_range construction from the convenience range functions
  3. """
  4. from datetime import (
  5. datetime,
  6. time,
  7. timedelta,
  8. )
  9. import numpy as np
  10. import pytest
  11. import pytz
  12. from pytz import timezone
  13. from pandas._libs.tslibs import timezones
  14. from pandas._libs.tslibs.offsets import (
  15. BDay,
  16. CDay,
  17. DateOffset,
  18. MonthEnd,
  19. prefix_mapping,
  20. )
  21. from pandas.errors import OutOfBoundsDatetime
  22. import pandas.util._test_decorators as td
  23. import pandas as pd
  24. from pandas import (
  25. DatetimeIndex,
  26. Timedelta,
  27. Timestamp,
  28. bdate_range,
  29. date_range,
  30. offsets,
  31. )
  32. import pandas._testing as tm
  33. from pandas.core.arrays.datetimes import _generate_range as generate_range
  34. START, END = datetime(2009, 1, 1), datetime(2010, 1, 1)
  35. def _get_expected_range(
  36. begin_to_match,
  37. end_to_match,
  38. both_range,
  39. inclusive_endpoints,
  40. ):
  41. """Helper to get expected range from a both inclusive range"""
  42. left_match = begin_to_match == both_range[0]
  43. right_match = end_to_match == both_range[-1]
  44. if inclusive_endpoints == "left" and right_match:
  45. expected_range = both_range[:-1]
  46. elif inclusive_endpoints == "right" and left_match:
  47. expected_range = both_range[1:]
  48. elif inclusive_endpoints == "neither" and left_match and right_match:
  49. expected_range = both_range[1:-1]
  50. elif inclusive_endpoints == "neither" and right_match:
  51. expected_range = both_range[:-1]
  52. elif inclusive_endpoints == "neither" and left_match:
  53. expected_range = both_range[1:]
  54. elif inclusive_endpoints == "both":
  55. expected_range = both_range[:]
  56. else:
  57. expected_range = both_range[:]
  58. return expected_range
  59. class TestTimestampEquivDateRange:
  60. # Older tests in TestTimeSeries constructed their `stamp` objects
  61. # using `date_range` instead of the `Timestamp` constructor.
  62. # TestTimestampEquivDateRange checks that these are equivalent in the
  63. # pertinent cases.
  64. def test_date_range_timestamp_equiv(self):
  65. rng = date_range("20090415", "20090519", tz="US/Eastern")
  66. stamp = rng[0]
  67. ts = Timestamp("20090415", tz="US/Eastern")
  68. assert ts == stamp
  69. def test_date_range_timestamp_equiv_dateutil(self):
  70. rng = date_range("20090415", "20090519", tz="dateutil/US/Eastern")
  71. stamp = rng[0]
  72. ts = Timestamp("20090415", tz="dateutil/US/Eastern")
  73. assert ts == stamp
  74. def test_date_range_timestamp_equiv_explicit_pytz(self):
  75. rng = date_range("20090415", "20090519", tz=pytz.timezone("US/Eastern"))
  76. stamp = rng[0]
  77. ts = Timestamp("20090415", tz=pytz.timezone("US/Eastern"))
  78. assert ts == stamp
  79. @td.skip_if_windows
  80. def test_date_range_timestamp_equiv_explicit_dateutil(self):
  81. from pandas._libs.tslibs.timezones import dateutil_gettz as gettz
  82. rng = date_range("20090415", "20090519", tz=gettz("US/Eastern"))
  83. stamp = rng[0]
  84. ts = Timestamp("20090415", tz=gettz("US/Eastern"))
  85. assert ts == stamp
  86. def test_date_range_timestamp_equiv_from_datetime_instance(self):
  87. datetime_instance = datetime(2014, 3, 4)
  88. # build a timestamp with a frequency, since then it supports
  89. # addition/subtraction of integers
  90. timestamp_instance = date_range(datetime_instance, periods=1, freq="D")[0]
  91. ts = Timestamp(datetime_instance)
  92. assert ts == timestamp_instance
  93. def test_date_range_timestamp_equiv_preserve_frequency(self):
  94. timestamp_instance = date_range("2014-03-05", periods=1, freq="D")[0]
  95. ts = Timestamp("2014-03-05")
  96. assert timestamp_instance == ts
  97. class TestDateRanges:
  98. @pytest.mark.parametrize("freq", ["N", "U", "L", "T", "S", "H", "D"])
  99. def test_date_range_edges(self, freq):
  100. # GH#13672
  101. td = Timedelta(f"1{freq}")
  102. ts = Timestamp("1970-01-01")
  103. idx = date_range(
  104. start=ts + td,
  105. end=ts + 4 * td,
  106. freq=freq,
  107. )
  108. exp = DatetimeIndex(
  109. [ts + n * td for n in range(1, 5)],
  110. freq=freq,
  111. )
  112. tm.assert_index_equal(idx, exp)
  113. # start after end
  114. idx = date_range(
  115. start=ts + 4 * td,
  116. end=ts + td,
  117. freq=freq,
  118. )
  119. exp = DatetimeIndex([], freq=freq)
  120. tm.assert_index_equal(idx, exp)
  121. # start matches end
  122. idx = date_range(
  123. start=ts + td,
  124. end=ts + td,
  125. freq=freq,
  126. )
  127. exp = DatetimeIndex([ts + td], freq=freq)
  128. tm.assert_index_equal(idx, exp)
  129. def test_date_range_near_implementation_bound(self):
  130. # GH#???
  131. freq = Timedelta(1)
  132. with pytest.raises(OutOfBoundsDatetime, match="Cannot generate range with"):
  133. date_range(end=Timestamp.min, periods=2, freq=freq)
  134. def test_date_range_nat(self):
  135. # GH#11587
  136. msg = "Neither `start` nor `end` can be NaT"
  137. with pytest.raises(ValueError, match=msg):
  138. date_range(start="2016-01-01", end=pd.NaT, freq="D")
  139. with pytest.raises(ValueError, match=msg):
  140. date_range(start=pd.NaT, end="2016-01-01", freq="D")
  141. def test_date_range_multiplication_overflow(self):
  142. # GH#24255
  143. # check that overflows in calculating `addend = periods * stride`
  144. # are caught
  145. with tm.assert_produces_warning(None):
  146. # we should _not_ be seeing a overflow RuntimeWarning
  147. dti = date_range(start="1677-09-22", periods=213503, freq="D")
  148. assert dti[0] == Timestamp("1677-09-22")
  149. assert len(dti) == 213503
  150. msg = "Cannot generate range with"
  151. with pytest.raises(OutOfBoundsDatetime, match=msg):
  152. date_range("1969-05-04", periods=200000000, freq="30000D")
  153. def test_date_range_unsigned_overflow_handling(self):
  154. # GH#24255
  155. # case where `addend = periods * stride` overflows int64 bounds
  156. # but not uint64 bounds
  157. dti = date_range(start="1677-09-22", end="2262-04-11", freq="D")
  158. dti2 = date_range(start=dti[0], periods=len(dti), freq="D")
  159. assert dti2.equals(dti)
  160. dti3 = date_range(end=dti[-1], periods=len(dti), freq="D")
  161. assert dti3.equals(dti)
  162. def test_date_range_int64_overflow_non_recoverable(self):
  163. # GH#24255
  164. # case with start later than 1970-01-01, overflow int64 but not uint64
  165. msg = "Cannot generate range with"
  166. with pytest.raises(OutOfBoundsDatetime, match=msg):
  167. date_range(start="1970-02-01", periods=106752 * 24, freq="H")
  168. # case with end before 1970-01-01, overflow int64 but not uint64
  169. with pytest.raises(OutOfBoundsDatetime, match=msg):
  170. date_range(end="1969-11-14", periods=106752 * 24, freq="H")
  171. @pytest.mark.slow
  172. def test_date_range_int64_overflow_stride_endpoint_different_signs(self):
  173. # cases where stride * periods overflow int64 and stride/endpoint
  174. # have different signs
  175. start = Timestamp("2262-02-23")
  176. end = Timestamp("1969-11-14")
  177. expected = date_range(start=start, end=end, freq="-1H")
  178. assert expected[0] == start
  179. assert expected[-1] == end
  180. dti = date_range(end=end, periods=len(expected), freq="-1H")
  181. tm.assert_index_equal(dti, expected)
  182. start2 = Timestamp("1970-02-01")
  183. end2 = Timestamp("1677-10-22")
  184. expected2 = date_range(start=start2, end=end2, freq="-1H")
  185. assert expected2[0] == start2
  186. assert expected2[-1] == end2
  187. dti2 = date_range(start=start2, periods=len(expected2), freq="-1H")
  188. tm.assert_index_equal(dti2, expected2)
  189. def test_date_range_out_of_bounds(self):
  190. # GH#14187
  191. msg = "Cannot generate range"
  192. with pytest.raises(OutOfBoundsDatetime, match=msg):
  193. date_range("2016-01-01", periods=100000, freq="D")
  194. with pytest.raises(OutOfBoundsDatetime, match=msg):
  195. date_range(end="1763-10-12", periods=100000, freq="D")
  196. def test_date_range_gen_error(self):
  197. rng = date_range("1/1/2000 00:00", "1/1/2000 00:18", freq="5min")
  198. assert len(rng) == 4
  199. @pytest.mark.parametrize("freq", ["AS", "YS"])
  200. def test_begin_year_alias(self, freq):
  201. # see gh-9313
  202. rng = date_range("1/1/2013", "7/1/2017", freq=freq)
  203. exp = DatetimeIndex(
  204. ["2013-01-01", "2014-01-01", "2015-01-01", "2016-01-01", "2017-01-01"],
  205. freq=freq,
  206. )
  207. tm.assert_index_equal(rng, exp)
  208. @pytest.mark.parametrize("freq", ["A", "Y"])
  209. def test_end_year_alias(self, freq):
  210. # see gh-9313
  211. rng = date_range("1/1/2013", "7/1/2017", freq=freq)
  212. exp = DatetimeIndex(
  213. ["2013-12-31", "2014-12-31", "2015-12-31", "2016-12-31"], freq=freq
  214. )
  215. tm.assert_index_equal(rng, exp)
  216. @pytest.mark.parametrize("freq", ["BA", "BY"])
  217. def test_business_end_year_alias(self, freq):
  218. # see gh-9313
  219. rng = date_range("1/1/2013", "7/1/2017", freq=freq)
  220. exp = DatetimeIndex(
  221. ["2013-12-31", "2014-12-31", "2015-12-31", "2016-12-30"], freq=freq
  222. )
  223. tm.assert_index_equal(rng, exp)
  224. def test_date_range_negative_freq(self):
  225. # GH 11018
  226. rng = date_range("2011-12-31", freq="-2A", periods=3)
  227. exp = DatetimeIndex(["2011-12-31", "2009-12-31", "2007-12-31"], freq="-2A")
  228. tm.assert_index_equal(rng, exp)
  229. assert rng.freq == "-2A"
  230. rng = date_range("2011-01-31", freq="-2M", periods=3)
  231. exp = DatetimeIndex(["2011-01-31", "2010-11-30", "2010-09-30"], freq="-2M")
  232. tm.assert_index_equal(rng, exp)
  233. assert rng.freq == "-2M"
  234. def test_date_range_bms_bug(self):
  235. # #1645
  236. rng = date_range("1/1/2000", periods=10, freq="BMS")
  237. ex_first = Timestamp("2000-01-03")
  238. assert rng[0] == ex_first
  239. def test_date_range_normalize(self):
  240. snap = datetime.today()
  241. n = 50
  242. rng = date_range(snap, periods=n, normalize=False, freq="2D")
  243. offset = timedelta(2)
  244. values = DatetimeIndex([snap + i * offset for i in range(n)], freq=offset)
  245. tm.assert_index_equal(rng, values)
  246. rng = date_range("1/1/2000 08:15", periods=n, normalize=False, freq="B")
  247. the_time = time(8, 15)
  248. for val in rng:
  249. assert val.time() == the_time
  250. def test_date_range_fy5252(self):
  251. dr = date_range(
  252. start="2013-01-01",
  253. periods=2,
  254. freq=offsets.FY5253(startingMonth=1, weekday=3, variation="nearest"),
  255. )
  256. assert dr[0] == Timestamp("2013-01-31")
  257. assert dr[1] == Timestamp("2014-01-30")
  258. def test_date_range_ambiguous_arguments(self):
  259. # #2538
  260. start = datetime(2011, 1, 1, 5, 3, 40)
  261. end = datetime(2011, 1, 1, 8, 9, 40)
  262. msg = (
  263. "Of the four parameters: start, end, periods, and "
  264. "freq, exactly three must be specified"
  265. )
  266. with pytest.raises(ValueError, match=msg):
  267. date_range(start, end, periods=10, freq="s")
  268. def test_date_range_convenience_periods(self):
  269. # GH 20808
  270. result = date_range("2018-04-24", "2018-04-27", periods=3)
  271. expected = DatetimeIndex(
  272. ["2018-04-24 00:00:00", "2018-04-25 12:00:00", "2018-04-27 00:00:00"],
  273. freq=None,
  274. )
  275. tm.assert_index_equal(result, expected)
  276. # Test if spacing remains linear if tz changes to dst in range
  277. result = date_range(
  278. "2018-04-01 01:00:00",
  279. "2018-04-01 04:00:00",
  280. tz="Australia/Sydney",
  281. periods=3,
  282. )
  283. expected = DatetimeIndex(
  284. [
  285. Timestamp("2018-04-01 01:00:00+1100", tz="Australia/Sydney"),
  286. Timestamp("2018-04-01 02:00:00+1000", tz="Australia/Sydney"),
  287. Timestamp("2018-04-01 04:00:00+1000", tz="Australia/Sydney"),
  288. ]
  289. )
  290. tm.assert_index_equal(result, expected)
  291. @pytest.mark.parametrize(
  292. "start,end,result_tz",
  293. [
  294. ["20180101", "20180103", "US/Eastern"],
  295. [datetime(2018, 1, 1), datetime(2018, 1, 3), "US/Eastern"],
  296. [Timestamp("20180101"), Timestamp("20180103"), "US/Eastern"],
  297. [
  298. Timestamp("20180101", tz="US/Eastern"),
  299. Timestamp("20180103", tz="US/Eastern"),
  300. "US/Eastern",
  301. ],
  302. [
  303. Timestamp("20180101", tz="US/Eastern"),
  304. Timestamp("20180103", tz="US/Eastern"),
  305. None,
  306. ],
  307. ],
  308. )
  309. def test_date_range_linspacing_tz(self, start, end, result_tz):
  310. # GH 20983
  311. result = date_range(start, end, periods=3, tz=result_tz)
  312. expected = date_range("20180101", periods=3, freq="D", tz="US/Eastern")
  313. tm.assert_index_equal(result, expected)
  314. def test_date_range_businesshour(self):
  315. idx = DatetimeIndex(
  316. [
  317. "2014-07-04 09:00",
  318. "2014-07-04 10:00",
  319. "2014-07-04 11:00",
  320. "2014-07-04 12:00",
  321. "2014-07-04 13:00",
  322. "2014-07-04 14:00",
  323. "2014-07-04 15:00",
  324. "2014-07-04 16:00",
  325. ],
  326. freq="BH",
  327. )
  328. rng = date_range("2014-07-04 09:00", "2014-07-04 16:00", freq="BH")
  329. tm.assert_index_equal(idx, rng)
  330. idx = DatetimeIndex(["2014-07-04 16:00", "2014-07-07 09:00"], freq="BH")
  331. rng = date_range("2014-07-04 16:00", "2014-07-07 09:00", freq="BH")
  332. tm.assert_index_equal(idx, rng)
  333. idx = DatetimeIndex(
  334. [
  335. "2014-07-04 09:00",
  336. "2014-07-04 10:00",
  337. "2014-07-04 11:00",
  338. "2014-07-04 12:00",
  339. "2014-07-04 13:00",
  340. "2014-07-04 14:00",
  341. "2014-07-04 15:00",
  342. "2014-07-04 16:00",
  343. "2014-07-07 09:00",
  344. "2014-07-07 10:00",
  345. "2014-07-07 11:00",
  346. "2014-07-07 12:00",
  347. "2014-07-07 13:00",
  348. "2014-07-07 14:00",
  349. "2014-07-07 15:00",
  350. "2014-07-07 16:00",
  351. "2014-07-08 09:00",
  352. "2014-07-08 10:00",
  353. "2014-07-08 11:00",
  354. "2014-07-08 12:00",
  355. "2014-07-08 13:00",
  356. "2014-07-08 14:00",
  357. "2014-07-08 15:00",
  358. "2014-07-08 16:00",
  359. ],
  360. freq="BH",
  361. )
  362. rng = date_range("2014-07-04 09:00", "2014-07-08 16:00", freq="BH")
  363. tm.assert_index_equal(idx, rng)
  364. def test_date_range_timedelta(self):
  365. start = "2020-01-01"
  366. end = "2020-01-11"
  367. rng1 = date_range(start, end, freq="3D")
  368. rng2 = date_range(start, end, freq=timedelta(days=3))
  369. tm.assert_index_equal(rng1, rng2)
  370. def test_range_misspecified(self):
  371. # GH #1095
  372. msg = (
  373. "Of the four parameters: start, end, periods, and "
  374. "freq, exactly three must be specified"
  375. )
  376. with pytest.raises(ValueError, match=msg):
  377. date_range(start="1/1/2000")
  378. with pytest.raises(ValueError, match=msg):
  379. date_range(end="1/1/2000")
  380. with pytest.raises(ValueError, match=msg):
  381. date_range(periods=10)
  382. with pytest.raises(ValueError, match=msg):
  383. date_range(start="1/1/2000", freq="H")
  384. with pytest.raises(ValueError, match=msg):
  385. date_range(end="1/1/2000", freq="H")
  386. with pytest.raises(ValueError, match=msg):
  387. date_range(periods=10, freq="H")
  388. with pytest.raises(ValueError, match=msg):
  389. date_range()
  390. def test_compat_replace(self):
  391. # https://github.com/statsmodels/statsmodels/issues/3349
  392. # replace should take ints/longs for compat
  393. result = date_range(Timestamp("1960-04-01 00:00:00"), periods=76, freq="QS-JAN")
  394. assert len(result) == 76
  395. def test_catch_infinite_loop(self):
  396. offset = offsets.DateOffset(minute=5)
  397. # blow up, don't loop forever
  398. msg = "Offset <DateOffset: minute=5> did not increment date"
  399. with pytest.raises(ValueError, match=msg):
  400. date_range(datetime(2011, 11, 11), datetime(2011, 11, 12), freq=offset)
  401. @pytest.mark.parametrize("periods", (1, 2))
  402. def test_wom_len(self, periods):
  403. # https://github.com/pandas-dev/pandas/issues/20517
  404. res = date_range(start="20110101", periods=periods, freq="WOM-1MON")
  405. assert len(res) == periods
  406. def test_construct_over_dst(self):
  407. # GH 20854
  408. pre_dst = Timestamp("2010-11-07 01:00:00").tz_localize(
  409. "US/Pacific", ambiguous=True
  410. )
  411. pst_dst = Timestamp("2010-11-07 01:00:00").tz_localize(
  412. "US/Pacific", ambiguous=False
  413. )
  414. expect_data = [
  415. Timestamp("2010-11-07 00:00:00", tz="US/Pacific"),
  416. pre_dst,
  417. pst_dst,
  418. ]
  419. expected = DatetimeIndex(expect_data, freq="H")
  420. result = date_range(start="2010-11-7", periods=3, freq="H", tz="US/Pacific")
  421. tm.assert_index_equal(result, expected)
  422. def test_construct_with_different_start_end_string_format(self):
  423. # GH 12064
  424. result = date_range(
  425. "2013-01-01 00:00:00+09:00", "2013/01/01 02:00:00+09:00", freq="H"
  426. )
  427. expected = DatetimeIndex(
  428. [
  429. Timestamp("2013-01-01 00:00:00+09:00"),
  430. Timestamp("2013-01-01 01:00:00+09:00"),
  431. Timestamp("2013-01-01 02:00:00+09:00"),
  432. ],
  433. freq="H",
  434. )
  435. tm.assert_index_equal(result, expected)
  436. def test_error_with_zero_monthends(self):
  437. msg = r"Offset <0 \* MonthEnds> did not increment date"
  438. with pytest.raises(ValueError, match=msg):
  439. date_range("1/1/2000", "1/1/2001", freq=MonthEnd(0))
  440. def test_range_bug(self):
  441. # GH #770
  442. offset = DateOffset(months=3)
  443. result = date_range("2011-1-1", "2012-1-31", freq=offset)
  444. start = datetime(2011, 1, 1)
  445. expected = DatetimeIndex([start + i * offset for i in range(5)], freq=offset)
  446. tm.assert_index_equal(result, expected)
  447. def test_range_tz_pytz(self):
  448. # see gh-2906
  449. tz = timezone("US/Eastern")
  450. start = tz.localize(datetime(2011, 1, 1))
  451. end = tz.localize(datetime(2011, 1, 3))
  452. dr = date_range(start=start, periods=3)
  453. assert dr.tz.zone == tz.zone
  454. assert dr[0] == start
  455. assert dr[2] == end
  456. dr = date_range(end=end, periods=3)
  457. assert dr.tz.zone == tz.zone
  458. assert dr[0] == start
  459. assert dr[2] == end
  460. dr = date_range(start=start, end=end)
  461. assert dr.tz.zone == tz.zone
  462. assert dr[0] == start
  463. assert dr[2] == end
  464. @pytest.mark.parametrize(
  465. "start, end",
  466. [
  467. [
  468. Timestamp(datetime(2014, 3, 6), tz="US/Eastern"),
  469. Timestamp(datetime(2014, 3, 12), tz="US/Eastern"),
  470. ],
  471. [
  472. Timestamp(datetime(2013, 11, 1), tz="US/Eastern"),
  473. Timestamp(datetime(2013, 11, 6), tz="US/Eastern"),
  474. ],
  475. ],
  476. )
  477. def test_range_tz_dst_straddle_pytz(self, start, end):
  478. dr = date_range(start, end, freq="D")
  479. assert dr[0] == start
  480. assert dr[-1] == end
  481. assert np.all(dr.hour == 0)
  482. dr = date_range(start, end, freq="D", tz="US/Eastern")
  483. assert dr[0] == start
  484. assert dr[-1] == end
  485. assert np.all(dr.hour == 0)
  486. dr = date_range(
  487. start.replace(tzinfo=None),
  488. end.replace(tzinfo=None),
  489. freq="D",
  490. tz="US/Eastern",
  491. )
  492. assert dr[0] == start
  493. assert dr[-1] == end
  494. assert np.all(dr.hour == 0)
  495. def test_range_tz_dateutil(self):
  496. # see gh-2906
  497. # Use maybe_get_tz to fix filename in tz under dateutil.
  498. from pandas._libs.tslibs.timezones import maybe_get_tz
  499. tz = lambda x: maybe_get_tz("dateutil/" + x)
  500. start = datetime(2011, 1, 1, tzinfo=tz("US/Eastern"))
  501. end = datetime(2011, 1, 3, tzinfo=tz("US/Eastern"))
  502. dr = date_range(start=start, periods=3)
  503. assert dr.tz == tz("US/Eastern")
  504. assert dr[0] == start
  505. assert dr[2] == end
  506. dr = date_range(end=end, periods=3)
  507. assert dr.tz == tz("US/Eastern")
  508. assert dr[0] == start
  509. assert dr[2] == end
  510. dr = date_range(start=start, end=end)
  511. assert dr.tz == tz("US/Eastern")
  512. assert dr[0] == start
  513. assert dr[2] == end
  514. @pytest.mark.parametrize("freq", ["1D", "3D", "2M", "7W", "3H", "A"])
  515. def test_range_closed(self, freq, inclusive_endpoints_fixture):
  516. begin = datetime(2011, 1, 1)
  517. end = datetime(2014, 1, 1)
  518. result_range = date_range(
  519. begin, end, inclusive=inclusive_endpoints_fixture, freq=freq
  520. )
  521. both_range = date_range(begin, end, inclusive="both", freq=freq)
  522. expected_range = _get_expected_range(
  523. begin, end, both_range, inclusive_endpoints_fixture
  524. )
  525. tm.assert_index_equal(expected_range, result_range)
  526. @pytest.mark.parametrize("freq", ["1D", "3D", "2M", "7W", "3H", "A"])
  527. def test_range_closed_with_tz_aware_start_end(
  528. self, freq, inclusive_endpoints_fixture
  529. ):
  530. # GH12409, GH12684
  531. begin = Timestamp("2011/1/1", tz="US/Eastern")
  532. end = Timestamp("2014/1/1", tz="US/Eastern")
  533. result_range = date_range(
  534. begin, end, inclusive=inclusive_endpoints_fixture, freq=freq
  535. )
  536. both_range = date_range(begin, end, inclusive="both", freq=freq)
  537. expected_range = _get_expected_range(
  538. begin,
  539. end,
  540. both_range,
  541. inclusive_endpoints_fixture,
  542. )
  543. tm.assert_index_equal(expected_range, result_range)
  544. @pytest.mark.parametrize("freq", ["1D", "3D", "2M", "7W", "3H", "A"])
  545. def test_range_with_tz_closed_with_tz_aware_start_end(
  546. self, freq, inclusive_endpoints_fixture
  547. ):
  548. begin = Timestamp("2011/1/1")
  549. end = Timestamp("2014/1/1")
  550. begintz = Timestamp("2011/1/1", tz="US/Eastern")
  551. endtz = Timestamp("2014/1/1", tz="US/Eastern")
  552. result_range = date_range(
  553. begin,
  554. end,
  555. inclusive=inclusive_endpoints_fixture,
  556. freq=freq,
  557. tz="US/Eastern",
  558. )
  559. both_range = date_range(
  560. begin, end, inclusive="both", freq=freq, tz="US/Eastern"
  561. )
  562. expected_range = _get_expected_range(
  563. begintz,
  564. endtz,
  565. both_range,
  566. inclusive_endpoints_fixture,
  567. )
  568. tm.assert_index_equal(expected_range, result_range)
  569. def test_range_closed_boundary(self, inclusive_endpoints_fixture):
  570. # GH#11804
  571. right_boundary = date_range(
  572. "2015-09-12",
  573. "2015-12-01",
  574. freq="QS-MAR",
  575. inclusive=inclusive_endpoints_fixture,
  576. )
  577. left_boundary = date_range(
  578. "2015-09-01",
  579. "2015-09-12",
  580. freq="QS-MAR",
  581. inclusive=inclusive_endpoints_fixture,
  582. )
  583. both_boundary = date_range(
  584. "2015-09-01",
  585. "2015-12-01",
  586. freq="QS-MAR",
  587. inclusive=inclusive_endpoints_fixture,
  588. )
  589. neither_boundary = date_range(
  590. "2015-09-11",
  591. "2015-09-12",
  592. freq="QS-MAR",
  593. inclusive=inclusive_endpoints_fixture,
  594. )
  595. expected_right = both_boundary
  596. expected_left = both_boundary
  597. expected_both = both_boundary
  598. if inclusive_endpoints_fixture == "right":
  599. expected_left = both_boundary[1:]
  600. elif inclusive_endpoints_fixture == "left":
  601. expected_right = both_boundary[:-1]
  602. elif inclusive_endpoints_fixture == "both":
  603. expected_right = both_boundary[1:]
  604. expected_left = both_boundary[:-1]
  605. expected_neither = both_boundary[1:-1]
  606. tm.assert_index_equal(right_boundary, expected_right)
  607. tm.assert_index_equal(left_boundary, expected_left)
  608. tm.assert_index_equal(both_boundary, expected_both)
  609. tm.assert_index_equal(neither_boundary, expected_neither)
  610. def test_years_only(self):
  611. # GH 6961
  612. dr = date_range("2014", "2015", freq="M")
  613. assert dr[0] == datetime(2014, 1, 31)
  614. assert dr[-1] == datetime(2014, 12, 31)
  615. def test_freq_divides_end_in_nanos(self):
  616. # GH 10885
  617. result_1 = date_range("2005-01-12 10:00", "2005-01-12 16:00", freq="345min")
  618. result_2 = date_range("2005-01-13 10:00", "2005-01-13 16:00", freq="345min")
  619. expected_1 = DatetimeIndex(
  620. ["2005-01-12 10:00:00", "2005-01-12 15:45:00"],
  621. dtype="datetime64[ns]",
  622. freq="345T",
  623. tz=None,
  624. )
  625. expected_2 = DatetimeIndex(
  626. ["2005-01-13 10:00:00", "2005-01-13 15:45:00"],
  627. dtype="datetime64[ns]",
  628. freq="345T",
  629. tz=None,
  630. )
  631. tm.assert_index_equal(result_1, expected_1)
  632. tm.assert_index_equal(result_2, expected_2)
  633. def test_cached_range_bug(self):
  634. rng = date_range("2010-09-01 05:00:00", periods=50, freq=DateOffset(hours=6))
  635. assert len(rng) == 50
  636. assert rng[0] == datetime(2010, 9, 1, 5)
  637. def test_timezone_comparison_bug(self):
  638. # smoke test
  639. start = Timestamp("20130220 10:00", tz="US/Eastern")
  640. result = date_range(start, periods=2, tz="US/Eastern")
  641. assert len(result) == 2
  642. def test_timezone_comparison_assert(self):
  643. start = Timestamp("20130220 10:00", tz="US/Eastern")
  644. msg = "Inferred time zone not equal to passed time zone"
  645. with pytest.raises(AssertionError, match=msg):
  646. date_range(start, periods=2, tz="Europe/Berlin")
  647. def test_negative_non_tick_frequency_descending_dates(self, tz_aware_fixture):
  648. # GH 23270
  649. tz = tz_aware_fixture
  650. result = date_range(start="2011-06-01", end="2011-01-01", freq="-1MS", tz=tz)
  651. expected = date_range(end="2011-06-01", start="2011-01-01", freq="1MS", tz=tz)[
  652. ::-1
  653. ]
  654. tm.assert_index_equal(result, expected)
  655. def test_range_where_start_equal_end(self, inclusive_endpoints_fixture):
  656. # GH 43394
  657. start = "2021-09-02"
  658. end = "2021-09-02"
  659. result = date_range(
  660. start=start, end=end, freq="D", inclusive=inclusive_endpoints_fixture
  661. )
  662. both_range = date_range(start=start, end=end, freq="D", inclusive="both")
  663. if inclusive_endpoints_fixture == "neither":
  664. expected = both_range[1:-1]
  665. elif inclusive_endpoints_fixture in ("left", "right", "both"):
  666. expected = both_range[:]
  667. tm.assert_index_equal(result, expected)
  668. class TestDateRangeTZ:
  669. """Tests for date_range with timezones"""
  670. def test_hongkong_tz_convert(self):
  671. # GH#1673 smoke test
  672. dr = date_range("2012-01-01", "2012-01-10", freq="D", tz="Hongkong")
  673. # it works!
  674. dr.hour
  675. @pytest.mark.parametrize("tzstr", ["US/Eastern", "dateutil/US/Eastern"])
  676. def test_date_range_span_dst_transition(self, tzstr):
  677. # GH#1778
  678. # Standard -> Daylight Savings Time
  679. dr = date_range("03/06/2012 00:00", periods=200, freq="W-FRI", tz="US/Eastern")
  680. assert (dr.hour == 0).all()
  681. dr = date_range("2012-11-02", periods=10, tz=tzstr)
  682. result = dr.hour
  683. expected = pd.Index([0] * 10, dtype="int32")
  684. tm.assert_index_equal(result, expected)
  685. @pytest.mark.parametrize("tzstr", ["US/Eastern", "dateutil/US/Eastern"])
  686. def test_date_range_timezone_str_argument(self, tzstr):
  687. tz = timezones.maybe_get_tz(tzstr)
  688. result = date_range("1/1/2000", periods=10, tz=tzstr)
  689. expected = date_range("1/1/2000", periods=10, tz=tz)
  690. tm.assert_index_equal(result, expected)
  691. def test_date_range_with_fixedoffset_noname(self):
  692. from pandas.tests.indexes.datetimes.test_timezones import fixed_off_no_name
  693. off = fixed_off_no_name
  694. start = datetime(2012, 3, 11, 5, 0, 0, tzinfo=off)
  695. end = datetime(2012, 6, 11, 5, 0, 0, tzinfo=off)
  696. rng = date_range(start=start, end=end)
  697. assert off == rng.tz
  698. idx = pd.Index([start, end])
  699. assert off == idx.tz
  700. @pytest.mark.parametrize("tzstr", ["US/Eastern", "dateutil/US/Eastern"])
  701. def test_date_range_with_tz(self, tzstr):
  702. stamp = Timestamp("3/11/2012 05:00", tz=tzstr)
  703. assert stamp.hour == 5
  704. rng = date_range("3/11/2012 04:00", periods=10, freq="H", tz=tzstr)
  705. assert stamp == rng[1]
  706. class TestGenRangeGeneration:
  707. def test_generate(self):
  708. rng1 = list(generate_range(START, END, periods=None, offset=BDay(), unit="ns"))
  709. rng2 = list(generate_range(START, END, periods=None, offset="B", unit="ns"))
  710. assert rng1 == rng2
  711. def test_generate_cday(self):
  712. rng1 = list(generate_range(START, END, periods=None, offset=CDay(), unit="ns"))
  713. rng2 = list(generate_range(START, END, periods=None, offset="C", unit="ns"))
  714. assert rng1 == rng2
  715. def test_1(self):
  716. rng = list(
  717. generate_range(
  718. start=datetime(2009, 3, 25),
  719. end=None,
  720. periods=2,
  721. offset=BDay(),
  722. unit="ns",
  723. )
  724. )
  725. expected = [datetime(2009, 3, 25), datetime(2009, 3, 26)]
  726. assert rng == expected
  727. def test_2(self):
  728. rng = list(
  729. generate_range(
  730. start=datetime(2008, 1, 1),
  731. end=datetime(2008, 1, 3),
  732. periods=None,
  733. offset=BDay(),
  734. unit="ns",
  735. )
  736. )
  737. expected = [datetime(2008, 1, 1), datetime(2008, 1, 2), datetime(2008, 1, 3)]
  738. assert rng == expected
  739. def test_3(self):
  740. rng = list(
  741. generate_range(
  742. start=datetime(2008, 1, 5),
  743. end=datetime(2008, 1, 6),
  744. periods=None,
  745. offset=BDay(),
  746. unit="ns",
  747. )
  748. )
  749. expected = []
  750. assert rng == expected
  751. def test_precision_finer_than_offset(self):
  752. # GH#9907
  753. result1 = date_range(
  754. start="2015-04-15 00:00:03", end="2016-04-22 00:00:00", freq="Q"
  755. )
  756. result2 = date_range(
  757. start="2015-04-15 00:00:03", end="2015-06-22 00:00:04", freq="W"
  758. )
  759. expected1_list = [
  760. "2015-06-30 00:00:03",
  761. "2015-09-30 00:00:03",
  762. "2015-12-31 00:00:03",
  763. "2016-03-31 00:00:03",
  764. ]
  765. expected2_list = [
  766. "2015-04-19 00:00:03",
  767. "2015-04-26 00:00:03",
  768. "2015-05-03 00:00:03",
  769. "2015-05-10 00:00:03",
  770. "2015-05-17 00:00:03",
  771. "2015-05-24 00:00:03",
  772. "2015-05-31 00:00:03",
  773. "2015-06-07 00:00:03",
  774. "2015-06-14 00:00:03",
  775. "2015-06-21 00:00:03",
  776. ]
  777. expected1 = DatetimeIndex(
  778. expected1_list, dtype="datetime64[ns]", freq="Q-DEC", tz=None
  779. )
  780. expected2 = DatetimeIndex(
  781. expected2_list, dtype="datetime64[ns]", freq="W-SUN", tz=None
  782. )
  783. tm.assert_index_equal(result1, expected1)
  784. tm.assert_index_equal(result2, expected2)
  785. dt1, dt2 = "2017-01-01", "2017-01-01"
  786. tz1, tz2 = "US/Eastern", "Europe/London"
  787. @pytest.mark.parametrize(
  788. "start,end",
  789. [
  790. (Timestamp(dt1, tz=tz1), Timestamp(dt2)),
  791. (Timestamp(dt1), Timestamp(dt2, tz=tz2)),
  792. (Timestamp(dt1, tz=tz1), Timestamp(dt2, tz=tz2)),
  793. (Timestamp(dt1, tz=tz2), Timestamp(dt2, tz=tz1)),
  794. ],
  795. )
  796. def test_mismatching_tz_raises_err(self, start, end):
  797. # issue 18488
  798. msg = "Start and end cannot both be tz-aware with different timezones"
  799. with pytest.raises(TypeError, match=msg):
  800. date_range(start, end)
  801. with pytest.raises(TypeError, match=msg):
  802. date_range(start, end, freq=BDay())
  803. class TestBusinessDateRange:
  804. def test_constructor(self):
  805. bdate_range(START, END, freq=BDay())
  806. bdate_range(START, periods=20, freq=BDay())
  807. bdate_range(end=START, periods=20, freq=BDay())
  808. msg = "periods must be a number, got B"
  809. with pytest.raises(TypeError, match=msg):
  810. date_range("2011-1-1", "2012-1-1", "B")
  811. with pytest.raises(TypeError, match=msg):
  812. bdate_range("2011-1-1", "2012-1-1", "B")
  813. msg = "freq must be specified for bdate_range; use date_range instead"
  814. with pytest.raises(TypeError, match=msg):
  815. bdate_range(START, END, periods=10, freq=None)
  816. def test_misc(self):
  817. end = datetime(2009, 5, 13)
  818. dr = bdate_range(end=end, periods=20)
  819. firstDate = end - 19 * BDay()
  820. assert len(dr) == 20
  821. assert dr[0] == firstDate
  822. assert dr[-1] == end
  823. def test_date_parse_failure(self):
  824. badly_formed_date = "2007/100/1"
  825. msg = "Unknown datetime string format, unable to parse: 2007/100/1"
  826. with pytest.raises(ValueError, match=msg):
  827. Timestamp(badly_formed_date)
  828. with pytest.raises(ValueError, match=msg):
  829. bdate_range(start=badly_formed_date, periods=10)
  830. with pytest.raises(ValueError, match=msg):
  831. bdate_range(end=badly_formed_date, periods=10)
  832. with pytest.raises(ValueError, match=msg):
  833. bdate_range(badly_formed_date, badly_formed_date)
  834. def test_daterange_bug_456(self):
  835. # GH #456
  836. rng1 = bdate_range("12/5/2011", "12/5/2011")
  837. rng2 = bdate_range("12/2/2011", "12/5/2011")
  838. assert rng2._data.freq == BDay()
  839. result = rng1.union(rng2)
  840. assert isinstance(result, DatetimeIndex)
  841. @pytest.mark.parametrize("inclusive", ["left", "right", "neither", "both"])
  842. def test_bdays_and_open_boundaries(self, inclusive):
  843. # GH 6673
  844. start = "2018-07-21" # Saturday
  845. end = "2018-07-29" # Sunday
  846. result = date_range(start, end, freq="B", inclusive=inclusive)
  847. bday_start = "2018-07-23" # Monday
  848. bday_end = "2018-07-27" # Friday
  849. expected = date_range(bday_start, bday_end, freq="D")
  850. tm.assert_index_equal(result, expected)
  851. # Note: we do _not_ expect the freqs to match here
  852. def test_bday_near_overflow(self):
  853. # GH#24252 avoid doing unnecessary addition that _would_ overflow
  854. start = Timestamp.max.floor("D").to_pydatetime()
  855. rng = date_range(start, end=None, periods=1, freq="B")
  856. expected = DatetimeIndex([start], freq="B")
  857. tm.assert_index_equal(rng, expected)
  858. def test_bday_overflow_error(self):
  859. # GH#24252 check that we get OutOfBoundsDatetime and not OverflowError
  860. msg = "Out of bounds nanosecond timestamp"
  861. start = Timestamp.max.floor("D").to_pydatetime()
  862. with pytest.raises(OutOfBoundsDatetime, match=msg):
  863. date_range(start, periods=2, freq="B")
  864. class TestCustomDateRange:
  865. def test_constructor(self):
  866. bdate_range(START, END, freq=CDay())
  867. bdate_range(START, periods=20, freq=CDay())
  868. bdate_range(end=START, periods=20, freq=CDay())
  869. msg = "periods must be a number, got C"
  870. with pytest.raises(TypeError, match=msg):
  871. date_range("2011-1-1", "2012-1-1", "C")
  872. with pytest.raises(TypeError, match=msg):
  873. bdate_range("2011-1-1", "2012-1-1", "C")
  874. def test_misc(self):
  875. end = datetime(2009, 5, 13)
  876. dr = bdate_range(end=end, periods=20, freq="C")
  877. firstDate = end - 19 * CDay()
  878. assert len(dr) == 20
  879. assert dr[0] == firstDate
  880. assert dr[-1] == end
  881. def test_daterange_bug_456(self):
  882. # GH #456
  883. rng1 = bdate_range("12/5/2011", "12/5/2011", freq="C")
  884. rng2 = bdate_range("12/2/2011", "12/5/2011", freq="C")
  885. assert rng2._data.freq == CDay()
  886. result = rng1.union(rng2)
  887. assert isinstance(result, DatetimeIndex)
  888. def test_cdaterange(self):
  889. result = bdate_range("2013-05-01", periods=3, freq="C")
  890. expected = DatetimeIndex(["2013-05-01", "2013-05-02", "2013-05-03"], freq="C")
  891. tm.assert_index_equal(result, expected)
  892. assert result.freq == expected.freq
  893. def test_cdaterange_weekmask(self):
  894. result = bdate_range(
  895. "2013-05-01", periods=3, freq="C", weekmask="Sun Mon Tue Wed Thu"
  896. )
  897. expected = DatetimeIndex(
  898. ["2013-05-01", "2013-05-02", "2013-05-05"], freq=result.freq
  899. )
  900. tm.assert_index_equal(result, expected)
  901. assert result.freq == expected.freq
  902. # raise with non-custom freq
  903. msg = (
  904. "a custom frequency string is required when holidays or "
  905. "weekmask are passed, got frequency B"
  906. )
  907. with pytest.raises(ValueError, match=msg):
  908. bdate_range("2013-05-01", periods=3, weekmask="Sun Mon Tue Wed Thu")
  909. def test_cdaterange_holidays(self):
  910. result = bdate_range("2013-05-01", periods=3, freq="C", holidays=["2013-05-01"])
  911. expected = DatetimeIndex(
  912. ["2013-05-02", "2013-05-03", "2013-05-06"], freq=result.freq
  913. )
  914. tm.assert_index_equal(result, expected)
  915. assert result.freq == expected.freq
  916. # raise with non-custom freq
  917. msg = (
  918. "a custom frequency string is required when holidays or "
  919. "weekmask are passed, got frequency B"
  920. )
  921. with pytest.raises(ValueError, match=msg):
  922. bdate_range("2013-05-01", periods=3, holidays=["2013-05-01"])
  923. def test_cdaterange_weekmask_and_holidays(self):
  924. result = bdate_range(
  925. "2013-05-01",
  926. periods=3,
  927. freq="C",
  928. weekmask="Sun Mon Tue Wed Thu",
  929. holidays=["2013-05-01"],
  930. )
  931. expected = DatetimeIndex(
  932. ["2013-05-02", "2013-05-05", "2013-05-06"], freq=result.freq
  933. )
  934. tm.assert_index_equal(result, expected)
  935. assert result.freq == expected.freq
  936. # raise with non-custom freq
  937. msg = (
  938. "a custom frequency string is required when holidays or "
  939. "weekmask are passed, got frequency B"
  940. )
  941. with pytest.raises(ValueError, match=msg):
  942. bdate_range(
  943. "2013-05-01",
  944. periods=3,
  945. weekmask="Sun Mon Tue Wed Thu",
  946. holidays=["2013-05-01"],
  947. )
  948. @pytest.mark.parametrize(
  949. "freq", [freq for freq in prefix_mapping if freq.startswith("C")]
  950. )
  951. def test_all_custom_freq(self, freq):
  952. # should not raise
  953. bdate_range(
  954. START, END, freq=freq, weekmask="Mon Wed Fri", holidays=["2009-03-14"]
  955. )
  956. bad_freq = freq + "FOO"
  957. msg = f"invalid custom frequency string: {bad_freq}"
  958. with pytest.raises(ValueError, match=msg):
  959. bdate_range(START, END, freq=bad_freq)
  960. @pytest.mark.parametrize(
  961. "start_end",
  962. [
  963. ("2018-01-01T00:00:01.000Z", "2018-01-03T00:00:01.000Z"),
  964. ("2018-01-01T00:00:00.010Z", "2018-01-03T00:00:00.010Z"),
  965. ("2001-01-01T00:00:00.010Z", "2001-01-03T00:00:00.010Z"),
  966. ],
  967. )
  968. def test_range_with_millisecond_resolution(self, start_end):
  969. # https://github.com/pandas-dev/pandas/issues/24110
  970. start, end = start_end
  971. result = date_range(start=start, end=end, periods=2, inclusive="left")
  972. expected = DatetimeIndex([start])
  973. tm.assert_index_equal(result, expected)
  974. @pytest.mark.parametrize(
  975. "start,period,expected",
  976. [
  977. ("2022-07-23 00:00:00+02:00", 1, ["2022-07-25 00:00:00+02:00"]),
  978. ("2022-07-22 00:00:00+02:00", 1, ["2022-07-22 00:00:00+02:00"]),
  979. (
  980. "2022-07-22 00:00:00+02:00",
  981. 2,
  982. ["2022-07-22 00:00:00+02:00", "2022-07-25 00:00:00+02:00"],
  983. ),
  984. ],
  985. )
  986. def test_range_with_timezone_and_custombusinessday(self, start, period, expected):
  987. # GH49441
  988. result = date_range(start=start, periods=period, freq="C")
  989. expected = DatetimeIndex(expected)
  990. tm.assert_index_equal(result, expected)
  991. def test_date_range_with_custom_holidays():
  992. # GH 30593
  993. freq = offsets.CustomBusinessHour(start="15:00", holidays=["2020-11-26"])
  994. result = date_range(start="2020-11-25 15:00", periods=4, freq=freq)
  995. expected = DatetimeIndex(
  996. [
  997. "2020-11-25 15:00:00",
  998. "2020-11-25 16:00:00",
  999. "2020-11-27 15:00:00",
  1000. "2020-11-27 16:00:00",
  1001. ],
  1002. freq=freq,
  1003. )
  1004. tm.assert_index_equal(result, expected)
  1005. class TestDateRangeNonNano:
  1006. def test_date_range_reso_validation(self):
  1007. msg = "'unit' must be one of 's', 'ms', 'us', 'ns'"
  1008. with pytest.raises(ValueError, match=msg):
  1009. date_range("2016-01-01", "2016-03-04", periods=3, unit="h")
  1010. def test_date_range_freq_higher_than_reso(self):
  1011. # freq being higher-resolution than reso is a problem
  1012. msg = "Use a lower freq or a higher unit instead"
  1013. with pytest.raises(ValueError, match=msg):
  1014. # # TODO give a more useful or informative message?
  1015. date_range("2016-01-01", "2016-01-02", freq="ns", unit="ms")
  1016. def test_date_range_freq_matches_reso(self):
  1017. # GH#49106 matching reso is OK
  1018. dti = date_range("2016-01-01", "2016-01-01 00:00:01", freq="ms", unit="ms")
  1019. rng = np.arange(1_451_606_400_000, 1_451_606_401_001, dtype=np.int64)
  1020. expected = DatetimeIndex(rng.view("M8[ms]"), freq="ms")
  1021. tm.assert_index_equal(dti, expected)
  1022. dti = date_range("2016-01-01", "2016-01-01 00:00:01", freq="us", unit="us")
  1023. rng = np.arange(1_451_606_400_000_000, 1_451_606_401_000_001, dtype=np.int64)
  1024. expected = DatetimeIndex(rng.view("M8[us]"), freq="us")
  1025. tm.assert_index_equal(dti, expected)
  1026. dti = date_range("2016-01-01", "2016-01-01 00:00:00.001", freq="ns", unit="ns")
  1027. rng = np.arange(
  1028. 1_451_606_400_000_000_000, 1_451_606_400_001_000_001, dtype=np.int64
  1029. )
  1030. expected = DatetimeIndex(rng.view("M8[ns]"), freq="ns")
  1031. tm.assert_index_equal(dti, expected)
  1032. def test_date_range_freq_lower_than_endpoints(self):
  1033. start = Timestamp("2022-10-19 11:50:44.719781")
  1034. end = Timestamp("2022-10-19 11:50:47.066458")
  1035. # start and end cannot be cast to "s" unit without lossy rounding,
  1036. # so we do not allow this in date_range
  1037. with pytest.raises(ValueError, match="Cannot losslessly convert units"):
  1038. date_range(start, end, periods=3, unit="s")
  1039. # but we can losslessly cast to "us"
  1040. dti = date_range(start, end, periods=2, unit="us")
  1041. rng = np.array(
  1042. [start.as_unit("us")._value, end.as_unit("us")._value], dtype=np.int64
  1043. )
  1044. expected = DatetimeIndex(rng.view("M8[us]"))
  1045. tm.assert_index_equal(dti, expected)
  1046. def test_date_range_non_nano(self):
  1047. start = np.datetime64("1066-10-14") # Battle of Hastings
  1048. end = np.datetime64("2305-07-13") # Jean-Luc Picard's birthday
  1049. dti = date_range(start, end, freq="D", unit="s")
  1050. assert dti.freq == "D"
  1051. assert dti.dtype == "M8[s]"
  1052. exp = np.arange(
  1053. start.astype("M8[s]").view("i8"),
  1054. (end + 1).astype("M8[s]").view("i8"),
  1055. 24 * 3600,
  1056. ).view("M8[s]")
  1057. tm.assert_numpy_array_equal(dti.to_numpy(), exp)