Skip to content

Commit 23997f3

Browse files
committed
dt.time
1 parent f5ce172 commit 23997f3

File tree

11 files changed

+97
-18
lines changed

11 files changed

+97
-18
lines changed

pandas/_libs/lib.pyx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2621,7 +2621,7 @@ def maybe_convert_objects(ndarray[object] objects,
26212621
seen.object_ = True
26222622
break
26232623
elif PyTime_Check(val):
2624-
if convert_time:
2624+
if convert_non_numeric:
26252625
seen.time_ = True
26262626
else:
26272627
seen.object_ = True
@@ -2696,7 +2696,7 @@ def maybe_convert_objects(ndarray[object] objects,
26962696
if opt is True:
26972697
import pyarrow as pa
26982698

2699-
from pandas.core.arrays.arrow import ArrowDtype
2699+
from pandas.core.dtypes.dtypes import ArrowDtype
27002700

27012701
obj = pa.array(objects)
27022702
dtype = ArrowDtype(obj.type)

pandas/core/arrays/datetimes.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
import numpy as np
1515

16+
from pandas._config import get_option
17+
1618
from pandas._libs import (
1719
lib,
1820
tslib,
@@ -56,6 +58,7 @@
5658
DatetimeTZDtype,
5759
ExtensionDtype,
5860
PeriodDtype,
61+
ArrowDtype,
5962
)
6063
from pandas.core.dtypes.missing import isna
6164

@@ -1374,7 +1377,30 @@ def time(self) -> npt.NDArray[np.object_]:
13741377
# keeping their timezone and not using UTC
13751378
timestamps = self._local_timestamps()
13761379

1377-
return ints_to_pydatetime(timestamps, box="time", reso=self._creso)
1380+
result = ints_to_pydatetime(timestamps, box="time", reso=self._creso)
1381+
1382+
opt = get_option("future.infer_time")
1383+
if opt is None:
1384+
warnings.warn(
1385+
f"The behavior of {type(self).__name__}.time is deprecated. "
1386+
"In a future version, this will an array with pyarrow time "
1387+
"dtype instead of object dtype. To opt in to the future behavior, "
1388+
"set `pd.set_option('future.infer_time', True)`.",
1389+
FutureWarning,
1390+
stacklevel=find_stack_level(),
1391+
)
1392+
elif opt is True:
1393+
# TODO: optimize this to avoid going through ints_to_pydatetime
1394+
import pyarrow as pa
1395+
1396+
pa_type = pa.time64(self.unit)
1397+
result[self.isna()] = None
1398+
obj = pa.array(result, type=pa_type)
1399+
dtype = ArrowDtype(obj.type)
1400+
out = dtype.construct_array_type()(obj)
1401+
return out
1402+
1403+
return result
13781404

13791405
@property
13801406
def timetz(self) -> npt.NDArray[np.object_]:

pandas/core/construction.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@
5151
is_object_dtype,
5252
pandas_dtype,
5353
)
54-
from pandas.core.dtypes.dtypes import NumpyEADtype
54+
from pandas.core.dtypes.dtypes import (
55+
ArrowDtype,
56+
NumpyEADtype,
57+
)
5558
from pandas.core.dtypes.generic import (
5659
ABCDataFrame,
5760
ABCExtensionArray,
@@ -297,7 +300,6 @@ def array(
297300
PeriodArray,
298301
TimedeltaArray,
299302
)
300-
from pandas.core.arrays.arrow import ArrowDtype
301303
from pandas.core.arrays.string_ import StringDtype
302304

303305
if lib.is_scalar(data):

pandas/core/indexes/accessors.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,6 @@ def _delegate_property_get(self, name: str): # type: ignore[override]
101101
elif not is_list_like(result):
102102
return result
103103

104-
result = np.asarray(result)
105-
106104
if self.orig is not None:
107105
index = self.orig.index
108106
else:

pandas/tests/arrays/test_datetimes.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,14 @@ def test_to_pydatetime(self, dta_dti):
155155
def test_time_date(self, dta_dti, meth):
156156
dta, dti = dta_dti
157157

158-
result = getattr(dta, meth)
159-
expected = getattr(dti, meth)
158+
warn = None
159+
msg = "In a future version, this will an array with pyarrow time dtype"
160+
if meth == "time":
161+
warn = FutureWarning
162+
163+
with tm.assert_produces_warning(warn, match=msg):
164+
result = getattr(dta, meth)
165+
expected = getattr(dti, meth)
160166
tm.assert_numpy_array_equal(result, expected)
161167

162168
def test_format_native_types(self, unit, dtype, dta_dti):

pandas/tests/generic/test_finalize.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,14 @@ def test_datetime_method(method):
671671
def test_datetime_property(attr):
672672
s = pd.Series(pd.date_range("2000", periods=4))
673673
s.attrs = {"a": 1}
674-
result = getattr(s.dt, attr)
674+
675+
warn = None
676+
msg = "In a future version, this will an array with pyarrow time dtype"
677+
if attr == "time":
678+
warn = FutureWarning
679+
with tm.assert_produces_warning(warn, match=msg):
680+
result = getattr(s.dt, attr)
681+
675682
assert result.attrs == {"a": 1}
676683

677684

pandas/tests/indexes/datetimes/test_scalar_compat.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424
class TestDatetimeIndexOps:
2525
def test_dti_time(self):
2626
rng = date_range("1/1/2000", freq="12min", periods=10)
27-
result = pd.Index(rng).time
27+
msg = "In a future version, this will an array with pyarrow time dtype"
28+
with tm.assert_produces_warning(FutureWarning, match=msg):
29+
result = pd.Index(rng).time
2830
expected = [t.time() for t in rng]
2931
assert (result == expected).all()
3032

pandas/tests/indexes/datetimes/test_timezones.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -853,7 +853,10 @@ def test_time_accessor(self, dtype):
853853
expected = np.array([time(10, 20, 30), pd.NaT])
854854

855855
index = DatetimeIndex(["2018-06-04 10:20:30", pd.NaT], dtype=dtype)
856-
result = index.time
856+
857+
msg = "In a future version, this will an array with pyarrow time dtype"
858+
with tm.assert_produces_warning(FutureWarning, match=msg):
859+
result = index.time
857860

858861
tm.assert_numpy_array_equal(result, expected)
859862

pandas/tests/io/parser/test_parse_dates.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,9 @@ def test_date_col_as_index_col(all_parsers):
479479
if parser.engine == "pyarrow":
480480
# https://github.com/pandas-dev/pandas/issues/44231
481481
# pyarrow 6.0 starts to infer time type
482-
expected["X2"] = pd.to_datetime("1970-01-01" + expected["X2"]).dt.time
482+
msg = "In a future version, this will an array with pyarrow time dtype"
483+
with tm.assert_produces_warning(FutureWarning, match=msg):
484+
expected["X2"] = pd.to_datetime("1970-01-01" + expected["X2"]).dt.time
483485

484486
tm.assert_frame_equal(result, expected)
485487

pandas/tests/series/accessors/test_cat_accessor.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,12 @@ def test_dt_accessor_api_for_categorical(self, idx):
211211
tm.assert_equal(res, exp)
212212

213213
for attr in attr_names:
214-
res = getattr(cat.dt, attr)
215-
exp = getattr(ser.dt, attr)
214+
with warnings.catch_warnings():
215+
if attr == "time":
216+
# deprecated to return pyarrow time dtype
217+
warnings.simplefilter("ignore", FutureWarning)
218+
res = getattr(cat.dt, attr)
219+
exp = getattr(ser.dt, attr)
216220

217221
tm.assert_equal(res, exp)
218222

pandas/tests/series/accessors/test_dt_accessor.py

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import pytz
1313

1414
from pandas._libs.tslibs.timezones import maybe_get_tz
15+
from pandas.compat import pa_version_under7p0
1516
from pandas.errors import SettingWithCopyError
1617

1718
from pandas.core.dtypes.common import (
@@ -89,8 +90,15 @@ def get_expected(ser, prop):
8990
return result
9091
return Series(result, index=ser.index, name=ser.name, dtype=result.dtype)
9192

92-
left = getattr(ser.dt, name)
93-
right = get_expected(ser, name)
93+
if name == "time":
94+
msg = "In a future version, this will an array with pyarrow time dtype"
95+
with tm.assert_produces_warning(FutureWarning, match=msg):
96+
left = getattr(ser.dt, name)
97+
right = get_expected(ser, name)
98+
else:
99+
left = getattr(ser.dt, name)
100+
right = get_expected(ser, name)
101+
94102
if not (is_list_like(left) and is_list_like(right)):
95103
assert left == right
96104
elif isinstance(left, DataFrame):
@@ -672,10 +680,31 @@ def test_valid_dt_with_missing_values(self):
672680
)
673681
tm.assert_series_equal(result, expected)
674682

675-
result = ser.dt.time
683+
msg = "In a future version, this will an array with pyarrow time"
684+
with tm.assert_produces_warning(FutureWarning, match=msg):
685+
result = ser.dt.time
676686
expected = Series([time(0), time(0), pd.NaT, time(0), time(0)], dtype="object")
677687
tm.assert_series_equal(result, expected)
678688

689+
with pd.option_context("future.infer_time", False):
690+
with tm.assert_produces_warning(None):
691+
result = ser.dt.time
692+
tm.assert_series_equal(result, expected)
693+
694+
if pa_version_under7p0:
695+
return
696+
697+
with pd.option_context("future.infer_time", True):
698+
with tm.assert_produces_warning(None):
699+
result_pa = ser.dt.time
700+
701+
import pyarrow as pa
702+
703+
pa_dtype = pa.time64("ns")
704+
dtype = pd.ArrowDtype(pa_dtype)
705+
expected_pa = expected.astype(dtype)
706+
tm.assert_series_equal(result_pa, expected_pa)
707+
679708
def test_dt_accessor_api(self):
680709
# GH 9322
681710
from pandas.core.indexes.accessors import (

0 commit comments

Comments
 (0)