""" Tests for DatetimeIndex methods behaving like their Timestamp counterparts """ from datetime import datetime import numpy as np import pytest from pandas._libs.tslibs import ( OutOfBoundsDatetime, to_offset, ) from pandas._libs.tslibs.offsets import INVALID_FREQ_ERR_MSG import pandas as pd from pandas import ( DatetimeIndex, Timestamp, date_range, ) import pandas._testing as tm class TestDatetimeIndexOps: def test_dti_time(self): rng = date_range("1/1/2000", freq="12min", periods=10) result = pd.Index(rng).time expected = [t.time() for t in rng] assert (result == expected).all() def test_dti_date(self): rng = date_range("1/1/2000", freq="12H", periods=10) result = pd.Index(rng).date expected = [t.date() for t in rng] assert (result == expected).all() @pytest.mark.parametrize("data", [["1400-01-01"], [datetime(1400, 1, 1)]]) def test_dti_date_out_of_range(self, data): # GH#1475 msg = ( "^Out of bounds nanosecond timestamp: " "1400-01-01( 00:00:00)?, at position 0$" ) with pytest.raises(OutOfBoundsDatetime, match=msg): DatetimeIndex(data) @pytest.mark.parametrize( "field", [ "dayofweek", "day_of_week", "dayofyear", "day_of_year", "quarter", "days_in_month", "is_month_start", "is_month_end", "is_quarter_start", "is_quarter_end", "is_year_start", "is_year_end", ], ) def test_dti_timestamp_fields(self, field): # extra fields from DatetimeIndex like quarter and week idx = tm.makeDateIndex(100) expected = getattr(idx, field)[-1] result = getattr(Timestamp(idx[-1]), field) assert result == expected def test_dti_timestamp_isocalendar_fields(self): idx = tm.makeDateIndex(100) expected = tuple(idx.isocalendar().iloc[-1].to_list()) result = idx[-1].isocalendar() assert result == expected # ---------------------------------------------------------------- # DatetimeIndex.round def test_round_daily(self): dti = date_range("20130101 09:10:11", periods=5) result = dti.round("D") expected = date_range("20130101", periods=5) tm.assert_index_equal(result, expected) dti = dti.tz_localize("UTC").tz_convert("US/Eastern") result = dti.round("D") expected = date_range("20130101", periods=5).tz_localize("US/Eastern") tm.assert_index_equal(result, expected) result = dti.round("s") tm.assert_index_equal(result, dti) @pytest.mark.parametrize( "freq, error_msg", [ ("Y", " is a non-fixed frequency"), ("M", " is a non-fixed frequency"), ("foobar", "Invalid frequency: foobar"), ], ) def test_round_invalid(self, freq, error_msg): dti = date_range("20130101 09:10:11", periods=5) dti = dti.tz_localize("UTC").tz_convert("US/Eastern") with pytest.raises(ValueError, match=error_msg): dti.round(freq) def test_round(self, tz_naive_fixture): tz = tz_naive_fixture rng = date_range(start="2016-01-01", periods=5, freq="30Min", tz=tz) elt = rng[1] expected_rng = DatetimeIndex( [ Timestamp("2016-01-01 00:00:00", tz=tz), Timestamp("2016-01-01 00:00:00", tz=tz), Timestamp("2016-01-01 01:00:00", tz=tz), Timestamp("2016-01-01 02:00:00", tz=tz), Timestamp("2016-01-01 02:00:00", tz=tz), ] ) expected_elt = expected_rng[1] tm.assert_index_equal(rng.round(freq="H"), expected_rng) assert elt.round(freq="H") == expected_elt msg = INVALID_FREQ_ERR_MSG with pytest.raises(ValueError, match=msg): rng.round(freq="foo") with pytest.raises(ValueError, match=msg): elt.round(freq="foo") msg = " is a non-fixed frequency" with pytest.raises(ValueError, match=msg): rng.round(freq="M") with pytest.raises(ValueError, match=msg): elt.round(freq="M") # GH#14440 & GH#15578 index = DatetimeIndex(["2016-10-17 12:00:00.0015"], tz=tz) result = index.round("ms") expected = DatetimeIndex(["2016-10-17 12:00:00.002000"], tz=tz) tm.assert_index_equal(result, expected) for freq in ["us", "ns"]: tm.assert_index_equal(index, index.round(freq)) index = DatetimeIndex(["2016-10-17 12:00:00.00149"], tz=tz) result = index.round("ms") expected = DatetimeIndex(["2016-10-17 12:00:00.001000"], tz=tz) tm.assert_index_equal(result, expected) index = DatetimeIndex(["2016-10-17 12:00:00.001501031"]) result = index.round("10ns") expected = DatetimeIndex(["2016-10-17 12:00:00.001501030"]) tm.assert_index_equal(result, expected) with tm.assert_produces_warning(False): ts = "2016-10-17 12:00:00.001501031" DatetimeIndex([ts]).round("1010ns") def test_no_rounding_occurs(self, tz_naive_fixture): # GH 21262 tz = tz_naive_fixture rng = date_range(start="2016-01-01", periods=5, freq="2Min", tz=tz) expected_rng = DatetimeIndex( [ Timestamp("2016-01-01 00:00:00", tz=tz), Timestamp("2016-01-01 00:02:00", tz=tz), Timestamp("2016-01-01 00:04:00", tz=tz), Timestamp("2016-01-01 00:06:00", tz=tz), Timestamp("2016-01-01 00:08:00", tz=tz), ] ) tm.assert_index_equal(rng.round(freq="2T"), expected_rng) @pytest.mark.parametrize( "test_input, rounder, freq, expected", [ (["2117-01-01 00:00:45"], "floor", "15s", ["2117-01-01 00:00:45"]), (["2117-01-01 00:00:45"], "ceil", "15s", ["2117-01-01 00:00:45"]), ( ["2117-01-01 00:00:45.000000012"], "floor", "10ns", ["2117-01-01 00:00:45.000000010"], ), ( ["1823-01-01 00:00:01.000000012"], "ceil", "10ns", ["1823-01-01 00:00:01.000000020"], ), (["1823-01-01 00:00:01"], "floor", "1s", ["1823-01-01 00:00:01"]), (["1823-01-01 00:00:01"], "ceil", "1s", ["1823-01-01 00:00:01"]), (["2018-01-01 00:15:00"], "ceil", "15T", ["2018-01-01 00:15:00"]), (["2018-01-01 00:15:00"], "floor", "15T", ["2018-01-01 00:15:00"]), (["1823-01-01 03:00:00"], "ceil", "3H", ["1823-01-01 03:00:00"]), (["1823-01-01 03:00:00"], "floor", "3H", ["1823-01-01 03:00:00"]), ( ("NaT", "1823-01-01 00:00:01"), "floor", "1s", ("NaT", "1823-01-01 00:00:01"), ), ( ("NaT", "1823-01-01 00:00:01"), "ceil", "1s", ("NaT", "1823-01-01 00:00:01"), ), ], ) def test_ceil_floor_edge(self, test_input, rounder, freq, expected): dt = DatetimeIndex(list(test_input)) func = getattr(dt, rounder) result = func(freq) expected = DatetimeIndex(list(expected)) assert expected.equals(result) @pytest.mark.parametrize( "start, index_freq, periods", [("2018-01-01", "12H", 25), ("2018-01-01 0:0:0.124999", "1ns", 1000)], ) @pytest.mark.parametrize( "round_freq", [ "2ns", "3ns", "4ns", "5ns", "6ns", "7ns", "250ns", "500ns", "750ns", "1us", "19us", "250us", "500us", "750us", "1s", "2s", "3s", "12H", "1D", ], ) def test_round_int64(self, start, index_freq, periods, round_freq): dt = date_range(start=start, freq=index_freq, periods=periods) unit = to_offset(round_freq).nanos # test floor result = dt.floor(round_freq) diff = dt.asi8 - result.asi8 mod = result.asi8 % unit assert (mod == 0).all(), f"floor not a {round_freq} multiple" assert (0 <= diff).all() and (diff < unit).all(), "floor error" # test ceil result = dt.ceil(round_freq) diff = result.asi8 - dt.asi8 mod = result.asi8 % unit assert (mod == 0).all(), f"ceil not a {round_freq} multiple" assert (0 <= diff).all() and (diff < unit).all(), "ceil error" # test round result = dt.round(round_freq) diff = abs(result.asi8 - dt.asi8) mod = result.asi8 % unit assert (mod == 0).all(), f"round not a {round_freq} multiple" assert (diff <= unit // 2).all(), "round error" if unit % 2 == 0: assert ( result.asi8[diff == unit // 2] % 2 == 0 ).all(), "round half to even error" # ---------------------------------------------------------------- # DatetimeIndex.normalize def test_normalize(self): rng = date_range("1/1/2000 9:30", periods=10, freq="D") result = rng.normalize() expected = date_range("1/1/2000", periods=10, freq="D") tm.assert_index_equal(result, expected) arr_ns = np.array([1380585623454345752, 1380585612343234312]).astype( "datetime64[ns]" ) rng_ns = DatetimeIndex(arr_ns) rng_ns_normalized = rng_ns.normalize() arr_ns = np.array([1380585600000000000, 1380585600000000000]).astype( "datetime64[ns]" ) expected = DatetimeIndex(arr_ns) tm.assert_index_equal(rng_ns_normalized, expected) assert result.is_normalized assert not rng.is_normalized def test_normalize_nat(self): dti = DatetimeIndex([pd.NaT, Timestamp("2018-01-01 01:00:00")]) result = dti.normalize() expected = DatetimeIndex([pd.NaT, Timestamp("2018-01-01")]) tm.assert_index_equal(result, expected) class TestDateTimeIndexToJulianDate: def test_1700(self): dr = date_range(start=Timestamp("1710-10-01"), periods=5, freq="D") r1 = pd.Index([x.to_julian_date() for x in dr]) r2 = dr.to_julian_date() assert isinstance(r2, pd.Index) and r2.dtype == np.float64 tm.assert_index_equal(r1, r2) def test_2000(self): dr = date_range(start=Timestamp("2000-02-27"), periods=5, freq="D") r1 = pd.Index([x.to_julian_date() for x in dr]) r2 = dr.to_julian_date() assert isinstance(r2, pd.Index) and r2.dtype == np.float64 tm.assert_index_equal(r1, r2) def test_hour(self): dr = date_range(start=Timestamp("2000-02-27"), periods=5, freq="H") r1 = pd.Index([x.to_julian_date() for x in dr]) r2 = dr.to_julian_date() assert isinstance(r2, pd.Index) and r2.dtype == np.float64 tm.assert_index_equal(r1, r2) def test_minute(self): dr = date_range(start=Timestamp("2000-02-27"), periods=5, freq="T") r1 = pd.Index([x.to_julian_date() for x in dr]) r2 = dr.to_julian_date() assert isinstance(r2, pd.Index) and r2.dtype == np.float64 tm.assert_index_equal(r1, r2) def test_second(self): dr = date_range(start=Timestamp("2000-02-27"), periods=5, freq="S") r1 = pd.Index([x.to_julian_date() for x in dr]) r2 = dr.to_julian_date() assert isinstance(r2, pd.Index) and r2.dtype == np.float64 tm.assert_index_equal(r1, r2)