Skip to content

Commit a9c15d3

Browse files
gfyoungjorisvandenbossche
authored andcommitted
DEPR: Deprecate Timestamp.to_datetime (#14101)
* DEPR: Deprecate Timestamp.to_datetime * API: Issue real warning in to_pydatetime * DEPR: Deprecate NaT.to_datetime Closes gh-8254.
1 parent 5a20ea2 commit a9c15d3

File tree

10 files changed

+100
-40
lines changed

10 files changed

+100
-40
lines changed

doc/source/whatsnew/v0.19.0.txt

+2
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ API changes
436436
~~~~~~~~~~~
437437

438438

439+
- ``Timestamp.to_pydatetime`` will issue a ``UserWarning`` when ``warn=True``, and the instance has a non-zero number of nanoseconds (:issue:`14101`)
439440
- ``Panel.to_sparse`` will raise a ``NotImplementedError`` exception when called (:issue:`13778`)
440441
- ``Index.reshape`` will raise a ``NotImplementedError`` exception when called (:issue:`12882`)
441442
- Non-convertible dates in an excel date column will be returned without conversion and the column will be ``object`` dtype, rather than raising an exception (:issue:`10001`)
@@ -1060,6 +1061,7 @@ Deprecations
10601061
- ``Categorical.reshape`` has been deprecated and will be removed in a subsequent release (:issue:`12882`)
10611062
- ``Series.reshape`` has been deprecated and will be removed in a subsequent release (:issue:`12882`)
10621063

1064+
- ``Timestamp.to_datetime`` has been deprecated in favour of ``Timestamp.to_pydatetime`` (:issue:`8254`)
10631065
- ``Index.to_datetime`` and ``DatetimeIndex.to_datetime`` have been deprecated in favour of ``pd.to_datetime`` (:issue:`8254`)
10641066
- ``SparseList`` has been deprecated and will be removed in a future version (:issue:`13784`)
10651067
- ``DataFrame.to_html()`` and ``DataFrame.to_latex()`` have dropped the ``colSpace`` parameter in favor of ``col_space`` (:issue:`13857`)

pandas/io/tests/json/test_pandas.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -908,17 +908,17 @@ def test_tz_is_utc(self):
908908

909909
ts = Timestamp('2013-01-10 05:00:00Z')
910910
self.assertEqual(exp, pd.json.dumps(ts, iso_dates=True))
911-
dt = ts.to_datetime()
911+
dt = ts.to_pydatetime()
912912
self.assertEqual(exp, pd.json.dumps(dt, iso_dates=True))
913913

914914
ts = Timestamp('2013-01-10 00:00:00', tz='US/Eastern')
915915
self.assertEqual(exp, pd.json.dumps(ts, iso_dates=True))
916-
dt = ts.to_datetime()
916+
dt = ts.to_pydatetime()
917917
self.assertEqual(exp, pd.json.dumps(dt, iso_dates=True))
918918

919919
ts = Timestamp('2013-01-10 00:00:00-0500')
920920
self.assertEqual(exp, pd.json.dumps(ts, iso_dates=True))
921-
dt = ts.to_datetime()
921+
dt = ts.to_pydatetime()
922922
self.assertEqual(exp, pd.json.dumps(dt, iso_dates=True))
923923

924924
def test_tz_range_is_utc(self):

pandas/tests/indexes/test_base.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ def test_asof(self):
502502
d = self.dateIndex[-1]
503503
self.assertEqual(self.dateIndex.asof(d + timedelta(1)), d)
504504

505-
d = self.dateIndex[0].to_datetime()
505+
d = self.dateIndex[0].to_pydatetime()
506506
tm.assertIsInstance(self.dateIndex.asof(d), Timestamp)
507507

508508
def test_asof_datetime_partial(self):

pandas/tests/test_groupby.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -779,7 +779,7 @@ def test_get_group(self):
779779
g = df.groupby('DATE')
780780
key = list(g.groups)[0]
781781
result1 = g.get_group(key)
782-
result2 = g.get_group(Timestamp(key).to_datetime())
782+
result2 = g.get_group(Timestamp(key).to_pydatetime())
783783
result3 = g.get_group(str(Timestamp(key)))
784784
assert_frame_equal(result1, result2)
785785
assert_frame_equal(result1, result3)
@@ -788,7 +788,7 @@ def test_get_group(self):
788788

789789
key = list(g.groups)[0]
790790
result1 = g.get_group(key)
791-
result2 = g.get_group((Timestamp(key[0]).to_datetime(), key[1]))
791+
result2 = g.get_group((Timestamp(key[0]).to_pydatetime(), key[1]))
792792
result3 = g.get_group((str(Timestamp(key[0])), key[1]))
793793
assert_frame_equal(result1, result2)
794794
assert_frame_equal(result1, result3)

pandas/tseries/tests/test_base.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1127,11 +1127,11 @@ def test_subtraction_ops_with_tz(self):
11271127
# check that dt/dti subtraction ops with tz are validated
11281128
dti = date_range('20130101', periods=3)
11291129
ts = Timestamp('20130101')
1130-
dt = ts.to_datetime()
1130+
dt = ts.to_pydatetime()
11311131
dti_tz = date_range('20130101', periods=3).tz_localize('US/Eastern')
11321132
ts_tz = Timestamp('20130101').tz_localize('US/Eastern')
11331133
ts_tz2 = Timestamp('20130101').tz_localize('CET')
1134-
dt_tz = ts_tz.to_datetime()
1134+
dt_tz = ts_tz.to_pydatetime()
11351135
td = Timedelta('1 days')
11361136

11371137
def _check(result, expected):

pandas/tseries/tests/test_offsets.py

+26-4
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,19 @@ def _check_offsetfunc_works(self, offset, funcname, dt, expected,
261261
self.assertTrue(isinstance(result, Timestamp))
262262
self.assertEqual(result, expected)
263263

264-
# test nano second is preserved
265-
result = func(Timestamp(dt) + Nano(5))
264+
# see gh-14101
265+
exp_warning = None
266+
ts = Timestamp(dt) + Nano(5)
267+
268+
if (offset_s.__class__.__name__ == 'DateOffset' and
269+
(funcname == 'apply' or normalize) and
270+
ts.nanosecond > 0):
271+
exp_warning = UserWarning
272+
273+
# test nanosecond is preserved
274+
with tm.assert_produces_warning(exp_warning,
275+
check_stacklevel=False):
276+
result = func(ts)
266277
self.assertTrue(isinstance(result, Timestamp))
267278
if normalize is False:
268279
self.assertEqual(result, expected + Nano(5))
@@ -289,8 +300,19 @@ def _check_offsetfunc_works(self, offset, funcname, dt, expected,
289300
self.assertTrue(isinstance(result, Timestamp))
290301
self.assertEqual(result, expected_localize)
291302

292-
# test nano second is preserved
293-
result = func(Timestamp(dt, tz=tz) + Nano(5))
303+
# see gh-14101
304+
exp_warning = None
305+
ts = Timestamp(dt, tz=tz) + Nano(5)
306+
307+
if (offset_s.__class__.__name__ == 'DateOffset' and
308+
(funcname == 'apply' or normalize) and
309+
ts.nanosecond > 0):
310+
exp_warning = UserWarning
311+
312+
# test nanosecond is preserved
313+
with tm.assert_produces_warning(exp_warning,
314+
check_stacklevel=False):
315+
result = func(ts)
294316
self.assertTrue(isinstance(result, Timestamp))
295317
if normalize is False:
296318
self.assertEqual(result, expected_localize + Nano(5))

pandas/tseries/tests/test_timeseries.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -1020,7 +1020,13 @@ def test_NaT_methods(self):
10201020

10211021
for method in nat_methods:
10221022
if hasattr(NaT, method):
1023-
self.assertIs(getattr(NaT, method)(), NaT)
1023+
# see gh-8254
1024+
exp_warning = None
1025+
if method == 'to_datetime':
1026+
exp_warning = FutureWarning
1027+
with tm.assert_produces_warning(
1028+
exp_warning, check_stacklevel=False):
1029+
self.assertIs(getattr(NaT, method)(), NaT)
10241030

10251031
# GH 12300
10261032
self.assertEqual(NaT.isoformat(), 'NaT')

pandas/tseries/tests/test_timezones.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ def test_timestamp_to_datetime_tzoffset(self):
169169
from dateutil.tz import tzoffset
170170
tzinfo = tzoffset(None, 7200)
171171
expected = Timestamp('3/11/2012 04:00', tz=tzinfo)
172-
result = Timestamp(expected.to_datetime())
172+
result = Timestamp(expected.to_pydatetime())
173173
self.assertEqual(expected, result)
174174

175175
def test_timedelta_push_over_dst_boundary(self):

pandas/tseries/tests/test_tslib.py

+31-6
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,17 @@ def test_max_valid(self):
4747
def test_to_datetime_bijective(self):
4848
# Ensure that converting to datetime and back only loses precision
4949
# by going from nanoseconds to microseconds.
50-
self.assertEqual(
51-
Timestamp(Timestamp.max.to_pydatetime()).value / 1000,
52-
Timestamp.max.value / 1000)
53-
self.assertEqual(
54-
Timestamp(Timestamp.min.to_pydatetime()).value / 1000,
55-
Timestamp.min.value / 1000)
50+
exp_warning = None if Timestamp.max.nanosecond == 0 else UserWarning
51+
with tm.assert_produces_warning(exp_warning, check_stacklevel=False):
52+
self.assertEqual(
53+
Timestamp(Timestamp.max.to_pydatetime()).value / 1000,
54+
Timestamp.max.value / 1000)
55+
56+
exp_warning = None if Timestamp.min.nanosecond == 0 else UserWarning
57+
with tm.assert_produces_warning(exp_warning, check_stacklevel=False):
58+
self.assertEqual(
59+
Timestamp(Timestamp.min.to_pydatetime()).value / 1000,
60+
Timestamp.min.value / 1000)
5661

5762

5863
class TestTimestamp(tm.TestCase):
@@ -616,6 +621,26 @@ def test_pprint(self):
616621
'foo': 1}"""
617622
self.assertEqual(result, expected)
618623

624+
def to_datetime_depr(self):
625+
# see gh-8254
626+
ts = Timestamp('2011-01-01')
627+
628+
with tm.assert_produces_warning(FutureWarning,
629+
check_stacklevel=False):
630+
expected = datetime.datetime(2011, 1, 1)
631+
result = ts.to_datetime()
632+
self.assertEqual(result, expected)
633+
634+
def to_pydatetime_nonzero_nano(self):
635+
ts = Timestamp('2011-01-01 9:00:00.123456789')
636+
637+
# Warn the user of data loss (nanoseconds).
638+
with tm.assert_produces_warning(UserWarning,
639+
check_stacklevel=False):
640+
expected = datetime.datetime(2011, 1, 1, 9, 0, 0, 123456)
641+
result = ts.to_pydatetime()
642+
self.assertEqual(result, expected)
643+
619644

620645
class TestDatetimeParsingWrappers(tm.TestCase):
621646
def test_does_not_convert_mixed_integer(self):

pandas/tslib.pyx

+25-20
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# cython: profile=False
22

3+
import warnings
4+
35
cimport numpy as np
46
from numpy cimport (int8_t, int32_t, int64_t, import_array, ndarray,
57
NPY_INT64, NPY_DATETIME, NPY_TIMEDELTA)
@@ -637,22 +639,6 @@ class Timestamp(_Timestamp):
637639
return Timestamp(datetime.replace(self, **kwds),
638640
freq=self.freq)
639641

640-
def to_pydatetime(self, warn=True):
641-
"""
642-
If warn=True, issue warning if nanoseconds is nonzero
643-
"""
644-
cdef:
645-
pandas_datetimestruct dts
646-
_TSObject ts
647-
648-
if self.nanosecond != 0 and warn:
649-
print 'Warning: discarding nonzero nanoseconds'
650-
ts = convert_to_tsobject(self, self.tzinfo, None, 0, 0)
651-
652-
return datetime(ts.dts.year, ts.dts.month, ts.dts.day,
653-
ts.dts.hour, ts.dts.min, ts.dts.sec,
654-
ts.dts.us, ts.tzinfo)
655-
656642
def isoformat(self, sep='T'):
657643
base = super(_Timestamp, self).isoformat(sep=sep)
658644
if self.nanosecond == 0:
@@ -805,11 +791,11 @@ def _make_nan_func(func_name):
805791
f.__name__ = func_name
806792
return f
807793

808-
_nat_methods = ['date', 'now', 'replace', 'to_datetime', 'today']
794+
_nat_methods = ['date', 'now', 'replace', 'to_pydatetime', 'today']
809795

810796
_nan_methods = ['weekday', 'isoweekday', 'total_seconds']
811797

812-
_implemented_methods = ['to_datetime64', 'isoformat']
798+
_implemented_methods = ['to_datetime', 'to_datetime64', 'isoformat']
813799
_implemented_methods.extend(_nat_methods)
814800
_implemented_methods.extend(_nan_methods)
815801

@@ -986,7 +972,7 @@ cdef class _Timestamp(datetime):
986972
ots = other
987973
elif isinstance(other, datetime):
988974
if self.nanosecond == 0:
989-
val = self.to_datetime()
975+
val = self.to_pydatetime()
990976
return PyObject_RichCompareBool(val, other, op)
991977

992978
try:
@@ -1048,7 +1034,7 @@ cdef class _Timestamp(datetime):
10481034

10491035
cdef bint _compare_outside_nanorange(_Timestamp self, datetime other,
10501036
int op) except -1:
1051-
cdef datetime dtval = self.to_datetime()
1037+
cdef datetime dtval = self.to_pydatetime()
10521038

10531039
self._assert_tzawareness_compat(other)
10541040

@@ -1078,9 +1064,28 @@ cdef class _Timestamp(datetime):
10781064
raise TypeError('Cannot compare tz-naive and tz-aware timestamps')
10791065

10801066
cpdef datetime to_datetime(_Timestamp self):
1067+
"""
1068+
DEPRECATED: use :meth:`to_pydatetime` instead.
1069+
1070+
Convert a Timestamp object to a native Python datetime object.
1071+
"""
1072+
warnings.warn("to_datetime is deprecated. Use self.to_pydatetime()",
1073+
FutureWarning, stacklevel=2)
1074+
return self.to_pydatetime(warn=False)
1075+
1076+
cpdef datetime to_pydatetime(_Timestamp self, warn=True):
1077+
"""
1078+
Convert a Timestamp object to a native Python datetime object.
1079+
1080+
If warn=True, issue a warning if nanoseconds is nonzero.
1081+
"""
10811082
cdef:
10821083
pandas_datetimestruct dts
10831084
_TSObject ts
1085+
1086+
if self.nanosecond != 0 and warn:
1087+
warnings.warn("Discarding nonzero nanoseconds in conversion",
1088+
UserWarning, stacklevel=2)
10841089
ts = convert_to_tsobject(self, self.tzinfo, None, 0, 0)
10851090
dts = ts.dts
10861091
return datetime(dts.year, dts.month, dts.day,

0 commit comments

Comments
 (0)