test_datetimes.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742
  1. """
  2. Tests for DatetimeArray
  3. """
  4. from datetime import timedelta
  5. import operator
  6. try:
  7. from zoneinfo import ZoneInfo
  8. except ImportError:
  9. ZoneInfo = None
  10. import numpy as np
  11. import pytest
  12. from pandas._libs.tslibs import (
  13. npy_unit_to_abbrev,
  14. tz_compare,
  15. )
  16. from pandas.core.dtypes.dtypes import DatetimeTZDtype
  17. import pandas as pd
  18. import pandas._testing as tm
  19. from pandas.core.arrays import (
  20. DatetimeArray,
  21. TimedeltaArray,
  22. )
  23. class TestNonNano:
  24. @pytest.fixture(params=["s", "ms", "us"])
  25. def unit(self, request):
  26. """Fixture returning parametrized time units"""
  27. return request.param
  28. @pytest.fixture
  29. def dtype(self, unit, tz_naive_fixture):
  30. tz = tz_naive_fixture
  31. if tz is None:
  32. return np.dtype(f"datetime64[{unit}]")
  33. else:
  34. return DatetimeTZDtype(unit=unit, tz=tz)
  35. @pytest.fixture
  36. def dta_dti(self, unit, dtype):
  37. tz = getattr(dtype, "tz", None)
  38. dti = pd.date_range("2016-01-01", periods=55, freq="D", tz=tz)
  39. if tz is None:
  40. arr = np.asarray(dti).astype(f"M8[{unit}]")
  41. else:
  42. arr = np.asarray(dti.tz_convert("UTC").tz_localize(None)).astype(
  43. f"M8[{unit}]"
  44. )
  45. dta = DatetimeArray._simple_new(arr, dtype=dtype)
  46. return dta, dti
  47. @pytest.fixture
  48. def dta(self, dta_dti):
  49. dta, dti = dta_dti
  50. return dta
  51. def test_non_nano(self, unit, dtype):
  52. arr = np.arange(5, dtype=np.int64).view(f"M8[{unit}]")
  53. dta = DatetimeArray._simple_new(arr, dtype=dtype)
  54. assert dta.dtype == dtype
  55. assert dta[0].unit == unit
  56. assert tz_compare(dta.tz, dta[0].tz)
  57. assert (dta[0] == dta[:1]).all()
  58. @pytest.mark.parametrize(
  59. "field", DatetimeArray._field_ops + DatetimeArray._bool_ops
  60. )
  61. def test_fields(self, unit, field, dtype, dta_dti):
  62. dta, dti = dta_dti
  63. assert (dti == dta).all()
  64. res = getattr(dta, field)
  65. expected = getattr(dti._data, field)
  66. tm.assert_numpy_array_equal(res, expected)
  67. def test_normalize(self, unit):
  68. dti = pd.date_range("2016-01-01 06:00:00", periods=55, freq="D")
  69. arr = np.asarray(dti).astype(f"M8[{unit}]")
  70. dta = DatetimeArray._simple_new(arr, dtype=arr.dtype)
  71. assert not dta.is_normalized
  72. # TODO: simplify once we can just .astype to other unit
  73. exp = np.asarray(dti.normalize()).astype(f"M8[{unit}]")
  74. expected = DatetimeArray._simple_new(exp, dtype=exp.dtype)
  75. res = dta.normalize()
  76. tm.assert_extension_array_equal(res, expected)
  77. def test_simple_new_requires_match(self, unit):
  78. arr = np.arange(5, dtype=np.int64).view(f"M8[{unit}]")
  79. dtype = DatetimeTZDtype(unit, "UTC")
  80. dta = DatetimeArray._simple_new(arr, dtype=dtype)
  81. assert dta.dtype == dtype
  82. wrong = DatetimeTZDtype("ns", "UTC")
  83. with pytest.raises(AssertionError, match=""):
  84. DatetimeArray._simple_new(arr, dtype=wrong)
  85. def test_std_non_nano(self, unit):
  86. dti = pd.date_range("2016-01-01", periods=55, freq="D")
  87. arr = np.asarray(dti).astype(f"M8[{unit}]")
  88. dta = DatetimeArray._simple_new(arr, dtype=arr.dtype)
  89. # we should match the nano-reso std, but floored to our reso.
  90. res = dta.std()
  91. assert res._creso == dta._creso
  92. assert res == dti.std().floor(unit)
  93. @pytest.mark.filterwarnings("ignore:Converting to PeriodArray.*:UserWarning")
  94. def test_to_period(self, dta_dti):
  95. dta, dti = dta_dti
  96. result = dta.to_period("D")
  97. expected = dti._data.to_period("D")
  98. tm.assert_extension_array_equal(result, expected)
  99. def test_iter(self, dta):
  100. res = next(iter(dta))
  101. expected = dta[0]
  102. assert type(res) is pd.Timestamp
  103. assert res._value == expected._value
  104. assert res._creso == expected._creso
  105. assert res == expected
  106. def test_astype_object(self, dta):
  107. result = dta.astype(object)
  108. assert all(x._creso == dta._creso for x in result)
  109. assert all(x == y for x, y in zip(result, dta))
  110. def test_to_pydatetime(self, dta_dti):
  111. dta, dti = dta_dti
  112. result = dta.to_pydatetime()
  113. expected = dti.to_pydatetime()
  114. tm.assert_numpy_array_equal(result, expected)
  115. @pytest.mark.parametrize("meth", ["time", "timetz", "date"])
  116. def test_time_date(self, dta_dti, meth):
  117. dta, dti = dta_dti
  118. result = getattr(dta, meth)
  119. expected = getattr(dti, meth)
  120. tm.assert_numpy_array_equal(result, expected)
  121. def test_format_native_types(self, unit, dtype, dta_dti):
  122. # In this case we should get the same formatted values with our nano
  123. # version dti._data as we do with the non-nano dta
  124. dta, dti = dta_dti
  125. res = dta._format_native_types()
  126. exp = dti._data._format_native_types()
  127. tm.assert_numpy_array_equal(res, exp)
  128. def test_repr(self, dta_dti, unit):
  129. dta, dti = dta_dti
  130. assert repr(dta) == repr(dti._data).replace("[ns", f"[{unit}")
  131. # TODO: tests with td64
  132. def test_compare_mismatched_resolutions(self, comparison_op):
  133. # comparison that numpy gets wrong bc of silent overflows
  134. op = comparison_op
  135. iinfo = np.iinfo(np.int64)
  136. vals = np.array([iinfo.min, iinfo.min + 1, iinfo.max], dtype=np.int64)
  137. # Construct so that arr2[1] < arr[1] < arr[2] < arr2[2]
  138. arr = np.array(vals).view("M8[ns]")
  139. arr2 = arr.view("M8[s]")
  140. left = DatetimeArray._simple_new(arr, dtype=arr.dtype)
  141. right = DatetimeArray._simple_new(arr2, dtype=arr2.dtype)
  142. if comparison_op is operator.eq:
  143. expected = np.array([False, False, False])
  144. elif comparison_op is operator.ne:
  145. expected = np.array([True, True, True])
  146. elif comparison_op in [operator.lt, operator.le]:
  147. expected = np.array([False, False, True])
  148. else:
  149. expected = np.array([False, True, False])
  150. result = op(left, right)
  151. tm.assert_numpy_array_equal(result, expected)
  152. result = op(left[1], right)
  153. tm.assert_numpy_array_equal(result, expected)
  154. if op not in [operator.eq, operator.ne]:
  155. # check that numpy still gets this wrong; if it is fixed we may be
  156. # able to remove compare_mismatched_resolutions
  157. np_res = op(left._ndarray, right._ndarray)
  158. tm.assert_numpy_array_equal(np_res[1:], ~expected[1:])
  159. def test_add_mismatched_reso_doesnt_downcast(self):
  160. # https://github.com/pandas-dev/pandas/pull/48748#issuecomment-1260181008
  161. td = pd.Timedelta(microseconds=1)
  162. dti = pd.date_range("2016-01-01", periods=3) - td
  163. dta = dti._data.as_unit("us")
  164. res = dta + td.as_unit("us")
  165. # even though the result is an even number of days
  166. # (so we _could_ downcast to unit="s"), we do not.
  167. assert res.unit == "us"
  168. @pytest.mark.parametrize(
  169. "scalar",
  170. [
  171. timedelta(hours=2),
  172. pd.Timedelta(hours=2),
  173. np.timedelta64(2, "h"),
  174. np.timedelta64(2 * 3600 * 1000, "ms"),
  175. pd.offsets.Minute(120),
  176. pd.offsets.Hour(2),
  177. ],
  178. )
  179. def test_add_timedeltalike_scalar_mismatched_reso(self, dta_dti, scalar):
  180. dta, dti = dta_dti
  181. td = pd.Timedelta(scalar)
  182. exp_reso = max(dta._creso, td._creso)
  183. exp_unit = npy_unit_to_abbrev(exp_reso)
  184. expected = (dti + td)._data.as_unit(exp_unit)
  185. result = dta + scalar
  186. tm.assert_extension_array_equal(result, expected)
  187. result = scalar + dta
  188. tm.assert_extension_array_equal(result, expected)
  189. expected = (dti - td)._data.as_unit(exp_unit)
  190. result = dta - scalar
  191. tm.assert_extension_array_equal(result, expected)
  192. def test_sub_datetimelike_scalar_mismatch(self):
  193. dti = pd.date_range("2016-01-01", periods=3)
  194. dta = dti._data.as_unit("us")
  195. ts = dta[0].as_unit("s")
  196. result = dta - ts
  197. expected = (dti - dti[0])._data.as_unit("us")
  198. assert result.dtype == "m8[us]"
  199. tm.assert_extension_array_equal(result, expected)
  200. def test_sub_datetime64_reso_mismatch(self):
  201. dti = pd.date_range("2016-01-01", periods=3)
  202. left = dti._data.as_unit("s")
  203. right = left.as_unit("ms")
  204. result = left - right
  205. exp_values = np.array([0, 0, 0], dtype="m8[ms]")
  206. expected = TimedeltaArray._simple_new(
  207. exp_values,
  208. dtype=exp_values.dtype,
  209. )
  210. tm.assert_extension_array_equal(result, expected)
  211. result2 = right - left
  212. tm.assert_extension_array_equal(result2, expected)
  213. class TestDatetimeArrayComparisons:
  214. # TODO: merge this into tests/arithmetic/test_datetime64 once it is
  215. # sufficiently robust
  216. def test_cmp_dt64_arraylike_tznaive(self, comparison_op):
  217. # arbitrary tz-naive DatetimeIndex
  218. op = comparison_op
  219. dti = pd.date_range("2016-01-1", freq="MS", periods=9, tz=None)
  220. arr = DatetimeArray(dti)
  221. assert arr.freq == dti.freq
  222. assert arr.tz == dti.tz
  223. right = dti
  224. expected = np.ones(len(arr), dtype=bool)
  225. if comparison_op.__name__ in ["ne", "gt", "lt"]:
  226. # for these the comparisons should be all-False
  227. expected = ~expected
  228. result = op(arr, arr)
  229. tm.assert_numpy_array_equal(result, expected)
  230. for other in [
  231. right,
  232. np.array(right),
  233. list(right),
  234. tuple(right),
  235. right.astype(object),
  236. ]:
  237. result = op(arr, other)
  238. tm.assert_numpy_array_equal(result, expected)
  239. result = op(other, arr)
  240. tm.assert_numpy_array_equal(result, expected)
  241. class TestDatetimeArray:
  242. def test_astype_non_nano_tznaive(self):
  243. dti = pd.date_range("2016-01-01", periods=3)
  244. res = dti.astype("M8[s]")
  245. assert res.dtype == "M8[s]"
  246. dta = dti._data
  247. res = dta.astype("M8[s]")
  248. assert res.dtype == "M8[s]"
  249. assert isinstance(res, pd.core.arrays.DatetimeArray) # used to be ndarray
  250. def test_astype_non_nano_tzaware(self):
  251. dti = pd.date_range("2016-01-01", periods=3, tz="UTC")
  252. res = dti.astype("M8[s, US/Pacific]")
  253. assert res.dtype == "M8[s, US/Pacific]"
  254. dta = dti._data
  255. res = dta.astype("M8[s, US/Pacific]")
  256. assert res.dtype == "M8[s, US/Pacific]"
  257. # from non-nano to non-nano, preserving reso
  258. res2 = res.astype("M8[s, UTC]")
  259. assert res2.dtype == "M8[s, UTC]"
  260. assert not tm.shares_memory(res2, res)
  261. res3 = res.astype("M8[s, UTC]", copy=False)
  262. assert res2.dtype == "M8[s, UTC]"
  263. assert tm.shares_memory(res3, res)
  264. def test_astype_to_same(self):
  265. arr = DatetimeArray._from_sequence(
  266. ["2000"], dtype=DatetimeTZDtype(tz="US/Central")
  267. )
  268. result = arr.astype(DatetimeTZDtype(tz="US/Central"), copy=False)
  269. assert result is arr
  270. @pytest.mark.parametrize("dtype", ["datetime64[ns]", "datetime64[ns, UTC]"])
  271. @pytest.mark.parametrize(
  272. "other", ["datetime64[ns]", "datetime64[ns, UTC]", "datetime64[ns, CET]"]
  273. )
  274. def test_astype_copies(self, dtype, other):
  275. # https://github.com/pandas-dev/pandas/pull/32490
  276. ser = pd.Series([1, 2], dtype=dtype)
  277. orig = ser.copy()
  278. err = False
  279. if (dtype == "datetime64[ns]") ^ (other == "datetime64[ns]"):
  280. # deprecated in favor of tz_localize
  281. err = True
  282. if err:
  283. if dtype == "datetime64[ns]":
  284. msg = "Use obj.tz_localize instead or series.dt.tz_localize instead"
  285. else:
  286. msg = "from timezone-aware dtype to timezone-naive dtype"
  287. with pytest.raises(TypeError, match=msg):
  288. ser.astype(other)
  289. else:
  290. t = ser.astype(other)
  291. t[:] = pd.NaT
  292. tm.assert_series_equal(ser, orig)
  293. @pytest.mark.parametrize("dtype", [int, np.int32, np.int64, "uint32", "uint64"])
  294. def test_astype_int(self, dtype):
  295. arr = DatetimeArray._from_sequence([pd.Timestamp("2000"), pd.Timestamp("2001")])
  296. if np.dtype(dtype) != np.int64:
  297. with pytest.raises(TypeError, match=r"Do obj.astype\('int64'\)"):
  298. arr.astype(dtype)
  299. return
  300. result = arr.astype(dtype)
  301. expected = arr._ndarray.view("i8")
  302. tm.assert_numpy_array_equal(result, expected)
  303. def test_astype_to_sparse_dt64(self):
  304. # GH#50082
  305. dti = pd.date_range("2016-01-01", periods=4)
  306. dta = dti._data
  307. result = dta.astype("Sparse[datetime64[ns]]")
  308. assert result.dtype == "Sparse[datetime64[ns]]"
  309. assert (result == dta).all()
  310. def test_tz_setter_raises(self):
  311. arr = DatetimeArray._from_sequence(
  312. ["2000"], dtype=DatetimeTZDtype(tz="US/Central")
  313. )
  314. with pytest.raises(AttributeError, match="tz_localize"):
  315. arr.tz = "UTC"
  316. def test_setitem_str_impute_tz(self, tz_naive_fixture):
  317. # Like for getitem, if we are passed a naive-like string, we impute
  318. # our own timezone.
  319. tz = tz_naive_fixture
  320. data = np.array([1, 2, 3], dtype="M8[ns]")
  321. dtype = data.dtype if tz is None else DatetimeTZDtype(tz=tz)
  322. arr = DatetimeArray(data, dtype=dtype)
  323. expected = arr.copy()
  324. ts = pd.Timestamp("2020-09-08 16:50").tz_localize(tz)
  325. setter = str(ts.tz_localize(None))
  326. # Setting a scalar tznaive string
  327. expected[0] = ts
  328. arr[0] = setter
  329. tm.assert_equal(arr, expected)
  330. # Setting a listlike of tznaive strings
  331. expected[1] = ts
  332. arr[:2] = [setter, setter]
  333. tm.assert_equal(arr, expected)
  334. def test_setitem_different_tz_raises(self):
  335. # pre-2.0 we required exact tz match, in 2.0 we require only
  336. # tzawareness-match
  337. data = np.array([1, 2, 3], dtype="M8[ns]")
  338. arr = DatetimeArray(data, copy=False, dtype=DatetimeTZDtype(tz="US/Central"))
  339. with pytest.raises(TypeError, match="Cannot compare tz-naive and tz-aware"):
  340. arr[0] = pd.Timestamp("2000")
  341. ts = pd.Timestamp("2000", tz="US/Eastern")
  342. arr[0] = ts
  343. assert arr[0] == ts.tz_convert("US/Central")
  344. def test_setitem_clears_freq(self):
  345. a = DatetimeArray(pd.date_range("2000", periods=2, freq="D", tz="US/Central"))
  346. a[0] = pd.Timestamp("2000", tz="US/Central")
  347. assert a.freq is None
  348. @pytest.mark.parametrize(
  349. "obj",
  350. [
  351. pd.Timestamp("2021-01-01"),
  352. pd.Timestamp("2021-01-01").to_datetime64(),
  353. pd.Timestamp("2021-01-01").to_pydatetime(),
  354. ],
  355. )
  356. def test_setitem_objects(self, obj):
  357. # make sure we accept datetime64 and datetime in addition to Timestamp
  358. dti = pd.date_range("2000", periods=2, freq="D")
  359. arr = dti._data
  360. arr[0] = obj
  361. assert arr[0] == obj
  362. def test_repeat_preserves_tz(self):
  363. dti = pd.date_range("2000", periods=2, freq="D", tz="US/Central")
  364. arr = DatetimeArray(dti)
  365. repeated = arr.repeat([1, 1])
  366. # preserves tz and values, but not freq
  367. expected = DatetimeArray(arr.asi8, freq=None, dtype=arr.dtype)
  368. tm.assert_equal(repeated, expected)
  369. def test_value_counts_preserves_tz(self):
  370. dti = pd.date_range("2000", periods=2, freq="D", tz="US/Central")
  371. arr = DatetimeArray(dti).repeat([4, 3])
  372. result = arr.value_counts()
  373. # Note: not tm.assert_index_equal, since `freq`s do not match
  374. assert result.index.equals(dti)
  375. arr[-2] = pd.NaT
  376. result = arr.value_counts(dropna=False)
  377. expected = pd.Series([4, 2, 1], index=[dti[0], dti[1], pd.NaT], name="count")
  378. tm.assert_series_equal(result, expected)
  379. @pytest.mark.parametrize("method", ["pad", "backfill"])
  380. def test_fillna_preserves_tz(self, method):
  381. dti = pd.date_range("2000-01-01", periods=5, freq="D", tz="US/Central")
  382. arr = DatetimeArray(dti, copy=True)
  383. arr[2] = pd.NaT
  384. fill_val = dti[1] if method == "pad" else dti[3]
  385. expected = DatetimeArray._from_sequence(
  386. [dti[0], dti[1], fill_val, dti[3], dti[4]],
  387. dtype=DatetimeTZDtype(tz="US/Central"),
  388. )
  389. result = arr.fillna(method=method)
  390. tm.assert_extension_array_equal(result, expected)
  391. # assert that arr and dti were not modified in-place
  392. assert arr[2] is pd.NaT
  393. assert dti[2] == pd.Timestamp("2000-01-03", tz="US/Central")
  394. def test_fillna_2d(self):
  395. dti = pd.date_range("2016-01-01", periods=6, tz="US/Pacific")
  396. dta = dti._data.reshape(3, 2).copy()
  397. dta[0, 1] = pd.NaT
  398. dta[1, 0] = pd.NaT
  399. res1 = dta.fillna(method="pad")
  400. expected1 = dta.copy()
  401. expected1[1, 0] = dta[0, 0]
  402. tm.assert_extension_array_equal(res1, expected1)
  403. res2 = dta.fillna(method="backfill")
  404. expected2 = dta.copy()
  405. expected2 = dta.copy()
  406. expected2[1, 0] = dta[2, 0]
  407. expected2[0, 1] = dta[1, 1]
  408. tm.assert_extension_array_equal(res2, expected2)
  409. # with different ordering for underlying ndarray; behavior should
  410. # be unchanged
  411. dta2 = dta._from_backing_data(dta._ndarray.copy(order="F"))
  412. assert dta2._ndarray.flags["F_CONTIGUOUS"]
  413. assert not dta2._ndarray.flags["C_CONTIGUOUS"]
  414. tm.assert_extension_array_equal(dta, dta2)
  415. res3 = dta2.fillna(method="pad")
  416. tm.assert_extension_array_equal(res3, expected1)
  417. res4 = dta2.fillna(method="backfill")
  418. tm.assert_extension_array_equal(res4, expected2)
  419. # test the DataFrame method while we're here
  420. df = pd.DataFrame(dta)
  421. res = df.fillna(method="pad")
  422. expected = pd.DataFrame(expected1)
  423. tm.assert_frame_equal(res, expected)
  424. res = df.fillna(method="backfill")
  425. expected = pd.DataFrame(expected2)
  426. tm.assert_frame_equal(res, expected)
  427. def test_array_interface_tz(self):
  428. tz = "US/Central"
  429. data = DatetimeArray(pd.date_range("2017", periods=2, tz=tz))
  430. result = np.asarray(data)
  431. expected = np.array(
  432. [
  433. pd.Timestamp("2017-01-01T00:00:00", tz=tz),
  434. pd.Timestamp("2017-01-02T00:00:00", tz=tz),
  435. ],
  436. dtype=object,
  437. )
  438. tm.assert_numpy_array_equal(result, expected)
  439. result = np.asarray(data, dtype=object)
  440. tm.assert_numpy_array_equal(result, expected)
  441. result = np.asarray(data, dtype="M8[ns]")
  442. expected = np.array(
  443. ["2017-01-01T06:00:00", "2017-01-02T06:00:00"], dtype="M8[ns]"
  444. )
  445. tm.assert_numpy_array_equal(result, expected)
  446. def test_array_interface(self):
  447. data = DatetimeArray(pd.date_range("2017", periods=2))
  448. expected = np.array(
  449. ["2017-01-01T00:00:00", "2017-01-02T00:00:00"], dtype="datetime64[ns]"
  450. )
  451. result = np.asarray(data)
  452. tm.assert_numpy_array_equal(result, expected)
  453. result = np.asarray(data, dtype=object)
  454. expected = np.array(
  455. [pd.Timestamp("2017-01-01T00:00:00"), pd.Timestamp("2017-01-02T00:00:00")],
  456. dtype=object,
  457. )
  458. tm.assert_numpy_array_equal(result, expected)
  459. @pytest.mark.parametrize("index", [True, False])
  460. def test_searchsorted_different_tz(self, index):
  461. data = np.arange(10, dtype="i8") * 24 * 3600 * 10**9
  462. arr = DatetimeArray(data, freq="D").tz_localize("Asia/Tokyo")
  463. if index:
  464. arr = pd.Index(arr)
  465. expected = arr.searchsorted(arr[2])
  466. result = arr.searchsorted(arr[2].tz_convert("UTC"))
  467. assert result == expected
  468. expected = arr.searchsorted(arr[2:6])
  469. result = arr.searchsorted(arr[2:6].tz_convert("UTC"))
  470. tm.assert_equal(result, expected)
  471. @pytest.mark.parametrize("index", [True, False])
  472. def test_searchsorted_tzawareness_compat(self, index):
  473. data = np.arange(10, dtype="i8") * 24 * 3600 * 10**9
  474. arr = DatetimeArray(data, freq="D")
  475. if index:
  476. arr = pd.Index(arr)
  477. mismatch = arr.tz_localize("Asia/Tokyo")
  478. msg = "Cannot compare tz-naive and tz-aware datetime-like objects"
  479. with pytest.raises(TypeError, match=msg):
  480. arr.searchsorted(mismatch[0])
  481. with pytest.raises(TypeError, match=msg):
  482. arr.searchsorted(mismatch)
  483. with pytest.raises(TypeError, match=msg):
  484. mismatch.searchsorted(arr[0])
  485. with pytest.raises(TypeError, match=msg):
  486. mismatch.searchsorted(arr)
  487. @pytest.mark.parametrize(
  488. "other",
  489. [
  490. 1,
  491. np.int64(1),
  492. 1.0,
  493. np.timedelta64("NaT"),
  494. pd.Timedelta(days=2),
  495. "invalid",
  496. np.arange(10, dtype="i8") * 24 * 3600 * 10**9,
  497. np.arange(10).view("timedelta64[ns]") * 24 * 3600 * 10**9,
  498. pd.Timestamp("2021-01-01").to_period("D"),
  499. ],
  500. )
  501. @pytest.mark.parametrize("index", [True, False])
  502. def test_searchsorted_invalid_types(self, other, index):
  503. data = np.arange(10, dtype="i8") * 24 * 3600 * 10**9
  504. arr = DatetimeArray(data, freq="D")
  505. if index:
  506. arr = pd.Index(arr)
  507. msg = "|".join(
  508. [
  509. "searchsorted requires compatible dtype or scalar",
  510. "value should be a 'Timestamp', 'NaT', or array of those. Got",
  511. ]
  512. )
  513. with pytest.raises(TypeError, match=msg):
  514. arr.searchsorted(other)
  515. def test_shift_fill_value(self):
  516. dti = pd.date_range("2016-01-01", periods=3)
  517. dta = dti._data
  518. expected = DatetimeArray(np.roll(dta._ndarray, 1))
  519. fv = dta[-1]
  520. for fill_value in [fv, fv.to_pydatetime(), fv.to_datetime64()]:
  521. result = dta.shift(1, fill_value=fill_value)
  522. tm.assert_datetime_array_equal(result, expected)
  523. dta = dta.tz_localize("UTC")
  524. expected = expected.tz_localize("UTC")
  525. fv = dta[-1]
  526. for fill_value in [fv, fv.to_pydatetime()]:
  527. result = dta.shift(1, fill_value=fill_value)
  528. tm.assert_datetime_array_equal(result, expected)
  529. def test_shift_value_tzawareness_mismatch(self):
  530. dti = pd.date_range("2016-01-01", periods=3)
  531. dta = dti._data
  532. fv = dta[-1].tz_localize("UTC")
  533. for invalid in [fv, fv.to_pydatetime()]:
  534. with pytest.raises(TypeError, match="Cannot compare"):
  535. dta.shift(1, fill_value=invalid)
  536. dta = dta.tz_localize("UTC")
  537. fv = dta[-1].tz_localize(None)
  538. for invalid in [fv, fv.to_pydatetime(), fv.to_datetime64()]:
  539. with pytest.raises(TypeError, match="Cannot compare"):
  540. dta.shift(1, fill_value=invalid)
  541. def test_shift_requires_tzmatch(self):
  542. # pre-2.0 we required exact tz match, in 2.0 we require just
  543. # matching tzawareness
  544. dti = pd.date_range("2016-01-01", periods=3, tz="UTC")
  545. dta = dti._data
  546. fill_value = pd.Timestamp("2020-10-18 18:44", tz="US/Pacific")
  547. result = dta.shift(1, fill_value=fill_value)
  548. expected = dta.shift(1, fill_value=fill_value.tz_convert("UTC"))
  549. tm.assert_equal(result, expected)
  550. def test_tz_localize_t2d(self):
  551. dti = pd.date_range("1994-05-12", periods=12, tz="US/Pacific")
  552. dta = dti._data.reshape(3, 4)
  553. result = dta.tz_localize(None)
  554. expected = dta.ravel().tz_localize(None).reshape(dta.shape)
  555. tm.assert_datetime_array_equal(result, expected)
  556. roundtrip = expected.tz_localize("US/Pacific")
  557. tm.assert_datetime_array_equal(roundtrip, dta)
  558. easts = ["US/Eastern", "dateutil/US/Eastern"]
  559. if ZoneInfo is not None:
  560. try:
  561. tz = ZoneInfo("US/Eastern")
  562. except KeyError:
  563. # no tzdata
  564. pass
  565. else:
  566. easts.append(tz)
  567. @pytest.mark.parametrize("tz", easts)
  568. def test_iter_zoneinfo_fold(self, tz):
  569. # GH#49684
  570. utc_vals = np.array(
  571. [1320552000, 1320555600, 1320559200, 1320562800], dtype=np.int64
  572. )
  573. utc_vals *= 1_000_000_000
  574. dta = DatetimeArray(utc_vals).tz_localize("UTC").tz_convert(tz)
  575. left = dta[2]
  576. right = list(dta)[2]
  577. assert str(left) == str(right)
  578. # previously there was a bug where with non-pytz right would be
  579. # Timestamp('2011-11-06 01:00:00-0400', tz='US/Eastern')
  580. # while left would be
  581. # Timestamp('2011-11-06 01:00:00-0500', tz='US/Eastern')
  582. # The .value's would match (so they would compare as equal),
  583. # but the folds would not
  584. assert left.utcoffset() == right.utcoffset()
  585. # The same bug in ints_to_pydatetime affected .astype, so we test
  586. # that here.
  587. right2 = dta.astype(object)[2]
  588. assert str(left) == str(right2)
  589. assert left.utcoffset() == right2.utcoffset()