Skip to content

Commit 8b8826c

Browse files
jbrockmendeljreback
authored andcommitted
DEPR: DTI int64 values and tz interpreted as UTC (#30115)
1 parent 9223ff5 commit 8b8826c

File tree

6 files changed

+28
-79
lines changed

6 files changed

+28
-79
lines changed

doc/source/whatsnew/v1.0.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,7 @@ or ``matplotlib.Axes.plot``. See :ref:`plotting.formatters` for more.
546546
- Removed the previously deprecated "by" keyword from :meth:`DataFrame.sort_index`, use :meth:`DataFrame.sort_values` instead (:issue:`10726`)
547547
- Removed support for nested renaming in :meth:`DataFrame.aggregate`, :meth:`Series.aggregate`, :meth:`DataFrameGroupBy.aggregate`, :meth:`SeriesGroupBy.aggregate`, :meth:`Rolling.aggregate` (:issue:`18529`)
548548
- Passing ``datetime64`` data to :class:`TimedeltaIndex` or ``timedelta64`` data to ``DatetimeIndex`` now raises ``TypeError`` (:issue:`23539`, :issue:`23937`)
549+
- Passing ``int64`` values to :class:`DatetimeIndex` and a timezone now interprets the values as nanosecond timestamps in UTC, not wall times in the given timezone (:issue:`24559`)
549550
- A tuple passed to :meth:`DataFrame.groupby` is now exclusively treated as a single key (:issue:`18314`)
550551
- Addition and subtraction of ``int`` or integer-arrays is no longer allowed in :class:`Timestamp`, :class:`DatetimeIndex`, :class:`TimedeltaIndex`, use ``obj + n * obj.freq`` instead of ``obj + n`` (:issue:`22535`)
551552
- Removed :meth:`Series.from_array` (:issue:`18258`)

pandas/core/arrays/datetimes.py

-35
Original file line numberDiff line numberDiff line change
@@ -55,21 +55,6 @@
5555
from pandas.tseries.offsets import Day, Tick
5656

5757
_midnight = time(0, 0)
58-
# TODO(GH-24559): Remove warning, int_as_wall_time parameter.
59-
_i8_message = """
60-
Passing integer-dtype data and a timezone to DatetimeIndex. Integer values
61-
will be interpreted differently in a future version of pandas. Previously,
62-
these were viewed as datetime64[ns] values representing the wall time
63-
*in the specified timezone*. In the future, these will be viewed as
64-
datetime64[ns] values representing the wall time *in UTC*. This is similar
65-
to a nanosecond-precision UNIX epoch. To accept the future behavior, use
66-
67-
pd.to_datetime(integer_data, utc=True).tz_convert(tz)
68-
69-
To keep the previous behavior, use
70-
71-
pd.to_datetime(integer_data).tz_localize(tz)
72-
"""
7358

7459

7560
def tz_to_dtype(tz):
@@ -422,7 +407,6 @@ def _from_sequence(
422407
dayfirst=False,
423408
yearfirst=False,
424409
ambiguous="raise",
425-
int_as_wall_time=False,
426410
):
427411

428412
freq, freq_infer = dtl.maybe_infer_freq(freq)
@@ -435,7 +419,6 @@ def _from_sequence(
435419
dayfirst=dayfirst,
436420
yearfirst=yearfirst,
437421
ambiguous=ambiguous,
438-
int_as_wall_time=int_as_wall_time,
439422
)
440423

441424
freq, freq_infer = dtl.validate_inferred_freq(freq, inferred_freq, freq_infer)
@@ -1811,7 +1794,6 @@ def sequence_to_dt64ns(
18111794
dayfirst=False,
18121795
yearfirst=False,
18131796
ambiguous="raise",
1814-
int_as_wall_time=False,
18151797
):
18161798
"""
18171799
Parameters
@@ -1824,13 +1806,6 @@ def sequence_to_dt64ns(
18241806
yearfirst : bool, default False
18251807
ambiguous : str, bool, or arraylike, default 'raise'
18261808
See pandas._libs.tslibs.conversion.tz_localize_to_utc.
1827-
int_as_wall_time : bool, default False
1828-
Whether to treat ints as wall time in specified timezone, or as
1829-
nanosecond-precision UNIX epoch (wall time in UTC).
1830-
This is used in DatetimeIndex.__init__ to deprecate the wall-time
1831-
behaviour.
1832-
1833-
..versionadded:: 0.24.0
18341809
18351810
Returns
18361811
-------
@@ -1891,10 +1866,6 @@ def sequence_to_dt64ns(
18911866
data, dayfirst=dayfirst, yearfirst=yearfirst
18921867
)
18931868
tz = maybe_infer_tz(tz, inferred_tz)
1894-
# When a sequence of timestamp objects is passed, we always
1895-
# want to treat the (now i8-valued) data as UTC timestamps,
1896-
# not wall times.
1897-
int_as_wall_time = False
18981869

18991870
# `data` may have originally been a Categorical[datetime64[ns, tz]],
19001871
# so we need to handle these types.
@@ -1928,12 +1899,6 @@ def sequence_to_dt64ns(
19281899

19291900
if data.dtype != _INT64_DTYPE:
19301901
data = data.astype(np.int64, copy=False)
1931-
if int_as_wall_time and tz is not None and not timezones.is_utc(tz):
1932-
warnings.warn(_i8_message, FutureWarning, stacklevel=4)
1933-
data = conversion.tz_localize_to_utc(
1934-
data.view("i8"), tz, ambiguous=ambiguous
1935-
)
1936-
data = data.view(_NS_DTYPE)
19371902
result = data.view(_NS_DTYPE)
19381903

19391904
if copy:

pandas/core/indexes/datetimes.py

-1
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,6 @@ def __new__(
295295
dayfirst=dayfirst,
296296
yearfirst=yearfirst,
297297
ambiguous=ambiguous,
298-
int_as_wall_time=True,
299298
)
300299

301300
subarr = cls._simple_new(dtarr, name=name, freq=dtarr.freq, tz=dtarr.tz)

pandas/tests/indexes/datetimes/test_construction.py

+12-24
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,11 @@ def test_construction_with_alt_tz_localize(self, kwargs, tz_aware_fixture):
122122
i = pd.date_range("20130101", periods=5, freq="H", tz=tz)
123123
kwargs = {key: attrgetter(val)(i) for key, val in kwargs.items()}
124124

125-
if str(tz) in ("UTC", "tzutc()", "UTC+00:00"):
126-
warn = None
127-
else:
128-
warn = FutureWarning
129-
130-
with tm.assert_produces_warning(warn, check_stacklevel=False):
131-
result = DatetimeIndex(i.tz_localize(None).asi8, **kwargs)
132-
expected = DatetimeIndex(i, **kwargs)
133-
tm.assert_index_equal(result, expected)
125+
if "tz" in kwargs:
126+
result = DatetimeIndex(i.asi8, tz="UTC").tz_convert(kwargs["tz"])
127+
128+
expected = DatetimeIndex(i, **kwargs)
129+
tm.assert_index_equal(result, expected)
134130

135131
# localize into the provided tz
136132
i2 = DatetimeIndex(i.tz_localize(None).asi8, tz="UTC")
@@ -485,11 +481,13 @@ def test_construction_with_ndarray(self):
485481
expected = DatetimeIndex(["2013-10-07", "2013-10-08", "2013-10-09"], freq="B")
486482
tm.assert_index_equal(result, expected)
487483

488-
def test_integer_values_and_tz_deprecated(self):
484+
def test_integer_values_and_tz_interpreted_as_utc(self):
489485
# GH-24559
490-
values = np.array([946684800000000000])
491-
with tm.assert_produces_warning(FutureWarning):
492-
result = DatetimeIndex(values, tz="US/Central")
486+
val = np.datetime64("2000-01-01 00:00:00", "ns")
487+
values = np.array([val.view("i8")])
488+
489+
result = DatetimeIndex(values).tz_localize("US/Central")
490+
493491
expected = pd.DatetimeIndex(["2000-01-01T00:00:00"], tz="US/Central")
494492
tm.assert_index_equal(result, expected)
495493

@@ -718,17 +716,7 @@ def test_constructor_timestamp_near_dst(self):
718716
@pytest.mark.parametrize("box", [np.array, partial(np.array, dtype=object), list])
719717
@pytest.mark.parametrize(
720718
"tz, dtype",
721-
[
722-
pytest.param(
723-
"US/Pacific",
724-
"datetime64[ns, US/Pacific]",
725-
marks=[
726-
pytest.mark.xfail(),
727-
pytest.mark.filterwarnings("ignore:\\n Passing:FutureWarning"),
728-
],
729-
),
730-
[None, "datetime64[ns]"],
731-
],
719+
[("US/Pacific", "datetime64[ns, US/Pacific]"), (None, "datetime64[ns]")],
732720
)
733721
def test_constructor_with_int_tz(self, klass, box, tz, dtype):
734722
# GH 20997, 20964

pandas/tests/indexes/multi/test_integrity.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,8 @@ def test_values_multiindex_datetimeindex():
4949
# Test to ensure we hit the boxing / nobox part of MI.values
5050
ints = np.arange(10 ** 18, 10 ** 18 + 5)
5151
naive = pd.DatetimeIndex(ints)
52-
# TODO(GH-24559): Remove the FutureWarning
53-
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
54-
aware = pd.DatetimeIndex(ints, tz="US/Central")
52+
53+
aware = pd.DatetimeIndex(ints, tz="US/Central")
5554

5655
idx = pd.MultiIndex.from_arrays([naive, aware])
5756
result = idx.values

pandas/tests/indexes/test_base.py

+13-16
Original file line numberDiff line numberDiff line change
@@ -453,9 +453,9 @@ def test_constructor_dtypes_to_timedelta(self, cast_index, vals):
453453
index = Index(vals)
454454
assert isinstance(index, TimedeltaIndex)
455455

456-
@pytest.mark.parametrize("attr, utc", [["values", False], ["asi8", True]])
456+
@pytest.mark.parametrize("attr", ["values", "asi8"])
457457
@pytest.mark.parametrize("klass", [pd.Index, pd.DatetimeIndex])
458-
def test_constructor_dtypes_datetime(self, tz_naive_fixture, attr, utc, klass):
458+
def test_constructor_dtypes_datetime(self, tz_naive_fixture, attr, klass):
459459
# Test constructing with a datetimetz dtype
460460
# .values produces numpy datetimes, so these are considered naive
461461
# .asi8 produces integers, so these are considered epoch timestamps
@@ -466,30 +466,27 @@ def test_constructor_dtypes_datetime(self, tz_naive_fixture, attr, utc, klass):
466466
index = index.tz_localize(tz_naive_fixture)
467467
dtype = index.dtype
468468

469-
if (
470-
tz_naive_fixture
471-
and attr == "asi8"
472-
and str(tz_naive_fixture) not in ("UTC", "tzutc()", "UTC+00:00")
473-
):
474-
ex_warn = FutureWarning
469+
if attr == "asi8":
470+
result = pd.DatetimeIndex(arg).tz_localize(tz_naive_fixture)
475471
else:
476-
ex_warn = None
477-
478-
# stacklevel is checked elsewhere. We don't do it here since
479-
# Index will have an frame, throwing off the expected.
480-
with tm.assert_produces_warning(ex_warn, check_stacklevel=False):
481472
result = klass(arg, tz=tz_naive_fixture)
482473
tm.assert_index_equal(result, index)
483474

484-
with tm.assert_produces_warning(ex_warn, check_stacklevel=False):
475+
if attr == "asi8":
476+
result = pd.DatetimeIndex(arg).astype(dtype)
477+
else:
485478
result = klass(arg, dtype=dtype)
486479
tm.assert_index_equal(result, index)
487480

488-
with tm.assert_produces_warning(ex_warn, check_stacklevel=False):
481+
if attr == "asi8":
482+
result = pd.DatetimeIndex(list(arg)).tz_localize(tz_naive_fixture)
483+
else:
489484
result = klass(list(arg), tz=tz_naive_fixture)
490485
tm.assert_index_equal(result, index)
491486

492-
with tm.assert_produces_warning(ex_warn, check_stacklevel=False):
487+
if attr == "asi8":
488+
result = pd.DatetimeIndex(list(arg)).astype(dtype)
489+
else:
493490
result = klass(list(arg), dtype=dtype)
494491
tm.assert_index_equal(result, index)
495492

0 commit comments

Comments
 (0)