From ce37a6e646b0f0fe43f912a2ecfdefe2476392af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dea=20Mar=C3=ADa=20L=C3=A9on?= Date: Mon, 19 Jun 2023 15:32:01 +0200 Subject: [PATCH 1/4] Examples Timestamp.time, timetuple, timetz, to_datetime64, toordinal --- pandas/_libs/tslibs/nattype.pyx | 72 ++++++++++++++++++-- pandas/_libs/tslibs/timestamps.pyx | 104 ++++++++++++++++++++++++++++- 2 files changed, 170 insertions(+), 6 deletions(-) diff --git a/pandas/_libs/tslibs/nattype.pyx b/pandas/_libs/tslibs/nattype.pyx index 75205a359db68..c6b93f4808588 100644 --- a/pandas/_libs/tslibs/nattype.pyx +++ b/pandas/_libs/tslibs/nattype.pyx @@ -256,7 +256,16 @@ cdef class _NaT(datetime): def to_datetime64(self) -> np.datetime64: """ - Return a numpy.datetime64 object with 'ns' precision. + Return a numpy.datetime64 object with same precision. + + Examples + -------- + >>> ts = pd.Timestamp(year=2023, month=1, day=1, + ... hour=10, second=15) + >>> ts + Timestamp('2023-01-01 10:00:15') + >>> ts.to_datetime64() + numpy.datetime64('2023-01-01T10:00:15.000000') """ return np.datetime64("NaT", "ns") @@ -509,12 +518,8 @@ class NaTType(_NaT): date = _make_nat_func("date", datetime.date.__doc__) utctimetuple = _make_error_func("utctimetuple", datetime) - timetz = _make_error_func("timetz", datetime) - timetuple = _make_error_func("timetuple", datetime) isocalendar = _make_error_func("isocalendar", datetime) dst = _make_error_func("dst", datetime) - time = _make_error_func("time", datetime) - toordinal = _make_error_func("toordinal", datetime) tzname = _make_error_func("tzname", datetime) utcoffset = _make_error_func("utcoffset", datetime) @@ -525,6 +530,63 @@ class NaTType(_NaT): # The remaining methods have docstrings copy/pasted from the analogous # Timestamp methods. + time = _make_error_func( + "time", + """ + Return time object with same time but with tzinfo=None. + + Examples + -------- + >>> ts = pd.Timestamp('2023-01-01 10:00:00') + >>> ts + Timestamp('2023-01-01 10:00:00') + >>> ts.time() + datetime.time(10, 0) + """, + ) + timetuple = _make_error_func( + "timetuple", + """ + Return time tuple, compatible with time.localtime(). + + Examples + -------- + >>> ts = pd.Timestamp('2023-01-01 10:00:00') + >>> ts + Timestamp('2023-01-01 10:00:00') + >>> ts.timetuple() + time.struct_time(tm_year=2023, tm_mon=1, tm_mday=1, + tm_hour=10, tm_min=0, tm_sec=0, tm_wday=6, tm_yday=1, tm_isdst=-1) + """ + ) + timetz = _make_error_func( + "timetz", + """ + Return time object with same time and tzinfo. + + Examples + -------- + >>> ts = pd.Timestamp('2023-01-01 10:00:00', tz='Europe/Brussels') + >>> ts + Timestamp('2023-01-01 10:00:00+0100', tz='Europe/Brussels') + >>> ts.timetz() + datetime.time(10, 0, tzinfo=) + """ + ) + toordinal = _make_error_func( + "toordinal", + """ + Return proleptic Gregorian ordinal. January 1 of year 1 is day 1. + + Examples + -------- + >>> ts = pd.Timestamp('2023-01-01 10:00:50') + >>> ts + Timestamp('2023-01-01 10:00:50') + >>> ts.toordinal() + 738521 + """ + ) ctime = _make_error_func( "ctime", """ diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index d66e856ef77cf..edcdcd6613a74 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -1202,7 +1202,16 @@ cdef class _Timestamp(ABCTimestamp): cpdef to_datetime64(self): """ - Return a numpy.datetime64 object with 'ns' precision. + Return a numpy.datetime64 object with same precision. + + Examples + -------- + >>> ts = pd.Timestamp(year=2023, month=1, day=1, + ... hour=10, second=15) + >>> ts + Timestamp('2023-01-01 10:00:15') + >>> ts.to_datetime64() + numpy.datetime64('2023-01-01T10:00:15.000000') """ # TODO: find a way to construct dt64 directly from _reso abbrev = npy_unit_to_abbrev(self._creso) @@ -1531,6 +1540,99 @@ class Timestamp(_Timestamp): ) from err return _dt.ctime() + def time(self): + """ + Return time object with same time but with tzinfo=None. + + Examples + -------- + >>> ts = pd.Timestamp('2023-01-01 10:00:00') + >>> ts + Timestamp('2023-01-01 10:00:00') + >>> ts.time() + datetime.time(10, 0) + """ + try: + _dt = datetime(self.year, self.month, self.day, + self.hour, self.minute, self.second, + self.microsecond, self.tzinfo, fold=self.fold) + except ValueError as err: + raise NotImplementedError( + "time not yet supported on Timestamps which " + "are outside the range of Python's standard library. " + ) from err + return _dt.time() + + def timetuple(self): + """ + Return time tuple, compatible with time.localtime(). + + Examples + -------- + >>> ts = pd.Timestamp('2023-01-01 10:00:00') + >>> ts + Timestamp('2023-01-01 10:00:00') + >>> ts.timetuple() + time.struct_time(tm_year=2023, tm_mon=1, tm_mday=1, + tm_hour=10, tm_min=0, tm_sec=0, tm_wday=6, tm_yday=1, tm_isdst=-1) + """ + try: + _dt = datetime(self.year, self.month, self.day, + self.hour, self.minute, self.second, + self.microsecond, self.tzinfo, fold=self.fold) + except ValueError as err: + raise NotImplementedError( + "timetuple not yet supported on Timestamps which " + "are outside the range of Python's standard library. " + ) from err + return _dt.timetuple() + + def timetz(self): + """ + Return time object with same time and tzinfo. + + Examples + -------- + >>> ts = pd.Timestamp('2023-01-01 10:00:00', tz='Europe/Brussels') + >>> ts + Timestamp('2023-01-01 10:00:00+0100', tz='Europe/Brussels') + >>> ts.timetz() + datetime.time(10, 0, tzinfo=) + """ + try: + _dt = datetime(self.year, self.month, self.day, + self.hour, self.minute, self.second, + self.microsecond, self.tzinfo, fold=self.fold) + except ValueError as err: + raise NotImplementedError( + "timetz not yet supported on Timestamps which " + "are outside the range of Python's standard library. " + ) from err + return _dt.timetz() + + def toordinal(self): + """ + Return proleptic Gregorian ordinal. January 1 of year 1 is day 1. + + Examples + -------- + >>> ts = pd.Timestamp('2023-01-01 10:00:50') + >>> ts + Timestamp('2023-01-01 10:00:50') + >>> ts.toordinal() + 738521 + """ + try: + _dt = datetime(self.year, self.month, self.day, + self.hour, self.minute, self.second, + self.microsecond, self.tzinfo, fold=self.fold) + except ValueError as err: + raise NotImplementedError( + "toordinal not yet supported on Timestamps which " + "are outside the range of Python's standard library. " + ) from err + return _dt.toordinal() + # Issue 25016. @classmethod def strptime(cls, date_string, format): From a4bc63116135b7ff1c234b592108e88b1008c9bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dea=20Mar=C3=ADa=20L=C3=A9on?= Date: Mon, 19 Jun 2023 16:29:24 +0200 Subject: [PATCH 2/4] Added tests and updated code_checks.sh --- ci/code_checks.sh | 5 ---- .../tests/scalar/timestamp/test_timestamp.py | 26 +++++++++++++++++-- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/ci/code_checks.sh b/ci/code_checks.sh index f63cc1fcc5767..963f31794d55c 100755 --- a/ci/code_checks.sh +++ b/ci/code_checks.sh @@ -110,11 +110,6 @@ if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then pandas.Timestamp.isocalendar \ pandas.Timestamp.isoweekday \ pandas.Timestamp.strptime \ - pandas.Timestamp.time \ - pandas.Timestamp.timetuple \ - pandas.Timestamp.timetz \ - pandas.Timestamp.to_datetime64 \ - pandas.Timestamp.toordinal \ pandas.Timestamp.tzname \ pandas.Timestamp.utcoffset \ pandas.Timestamp.utctimetuple \ diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index afb4dd7422114..135306a9c6d53 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -1124,9 +1124,31 @@ def test_negative_dates(): # https://github.com/pandas-dev/pandas/issues/50787 ts = Timestamp("-2000-01-01") msg = ( - "^strftime not yet supported on Timestamps which are outside the range of " + " not yet supported on Timestamps which are outside the range of " "Python's standard library. For now, please call the components you need " r"\(such as `.year` and `.month`\) and construct your string from there.$" ) - with pytest.raises(NotImplementedError, match=msg): + func = "^strftime" + with pytest.raises(NotImplementedError, match=func + msg): ts.strftime("%Y") + + msg = ( + " not yet supported on Timestamps which " + "are outside the range of Python's standard library. " + ) + + func = "^time" + with pytest.raises(NotImplementedError, match=func + msg): + ts.time() + + func = "^timetuple" + with pytest.raises(NotImplementedError, match=func + msg): + ts.timetuple() + + func = "^timetz" + with pytest.raises(NotImplementedError, match=func + msg): + ts.timetz() + + func = "^toordinal" + with pytest.raises(NotImplementedError, match=func + msg): + ts.toordinal() From 299a31cb91009586da17213d608f0c3efe4f50eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dea=20Mar=C3=ADa=20L=C3=A9on?= Date: Tue, 20 Jun 2023 12:12:58 +0200 Subject: [PATCH 3/4] Corrected time and timetz --- doc/source/whatsnew/v2.1.0.rst | 2 +- pandas/_libs/tslibs/timestamps.pyx | 28 ++++++------------- .../tests/scalar/timestamp/test_timestamp.py | 8 ------ 3 files changed, 9 insertions(+), 29 deletions(-) diff --git a/doc/source/whatsnew/v2.1.0.rst b/doc/source/whatsnew/v2.1.0.rst index 3c4ca23797f2c..3de885b974cd6 100644 --- a/doc/source/whatsnew/v2.1.0.rst +++ b/doc/source/whatsnew/v2.1.0.rst @@ -351,10 +351,10 @@ Datetimelike - :meth:`DatetimeIndex.map` with ``na_action="ignore"`` now works as expected. (:issue:`51644`) - Bug in :func:`date_range` when ``freq`` was a :class:`DateOffset` with ``nanoseconds`` (:issue:`46877`) - Bug in :meth:`Timestamp.round` with values close to the implementation bounds returning incorrect results instead of raising ``OutOfBoundsDatetime`` (:issue:`51494`) +- Bug in :meth:`Timestamp.timetuple` and :meth:`Timestamp.toordinal` were returning incorrect results for inputs outside those supported by the Python standard library's datetime module (:issue:`53668`) - Bug in :meth:`arrays.DatetimeArray.map` and :meth:`DatetimeIndex.map`, where the supplied callable operated array-wise instead of element-wise (:issue:`51977`) - Bug in constructing a :class:`Series` or :class:`DataFrame` from a datetime or timedelta scalar always inferring nanosecond resolution instead of inferring from the input (:issue:`52212`) - Bug in parsing datetime strings with weekday but no day e.g. "2023 Sept Thu" incorrectly raising ``AttributeError`` instead of ``ValueError`` (:issue:`52659`) -- Timedelta ^^^^^^^^^ diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index edcdcd6613a74..f380011817fb0 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -45,6 +45,8 @@ from cpython.object cimport ( import_datetime() +import datetime as dt + from pandas._libs.tslibs cimport ccalendar from pandas._libs.tslibs.base cimport ABCTimestamp @@ -1552,16 +1554,9 @@ class Timestamp(_Timestamp): >>> ts.time() datetime.time(10, 0) """ - try: - _dt = datetime(self.year, self.month, self.day, - self.hour, self.minute, self.second, - self.microsecond, self.tzinfo, fold=self.fold) - except ValueError as err: - raise NotImplementedError( - "time not yet supported on Timestamps which " - "are outside the range of Python's standard library. " - ) from err - return _dt.time() + _dt = dt.time(self.hour, self.minute, self.second, + self.microsecond, self.tzinfo, fold=self.fold) + return _dt def timetuple(self): """ @@ -1599,16 +1594,9 @@ class Timestamp(_Timestamp): >>> ts.timetz() datetime.time(10, 0, tzinfo=) """ - try: - _dt = datetime(self.year, self.month, self.day, - self.hour, self.minute, self.second, - self.microsecond, self.tzinfo, fold=self.fold) - except ValueError as err: - raise NotImplementedError( - "timetz not yet supported on Timestamps which " - "are outside the range of Python's standard library. " - ) from err - return _dt.timetz() + _dt = dt.time(self.hour, self.minute, self.second, + self.microsecond, self.tzinfo) + return _dt def toordinal(self): """ diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index 135306a9c6d53..e2468fb9e31f8 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -1137,18 +1137,10 @@ def test_negative_dates(): "are outside the range of Python's standard library. " ) - func = "^time" - with pytest.raises(NotImplementedError, match=func + msg): - ts.time() - func = "^timetuple" with pytest.raises(NotImplementedError, match=func + msg): ts.timetuple() - func = "^timetz" - with pytest.raises(NotImplementedError, match=func + msg): - ts.timetz() - func = "^toordinal" with pytest.raises(NotImplementedError, match=func + msg): ts.toordinal() From cf3a12cd61b6f804f6bd62a886c824e5554c62c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dea=20Mar=C3=ADa=20L=C3=A9on?= Date: Tue, 20 Jun 2023 18:52:18 +0200 Subject: [PATCH 4/4] Corrected Timestamp.time and timetz --- pandas/_libs/tslibs/timestamps.pyx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index f380011817fb0..e54b169eb53aa 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -45,8 +45,6 @@ from cpython.object cimport ( import_datetime() -import datetime as dt - from pandas._libs.tslibs cimport ccalendar from pandas._libs.tslibs.base cimport ABCTimestamp @@ -1554,9 +1552,7 @@ class Timestamp(_Timestamp): >>> ts.time() datetime.time(10, 0) """ - _dt = dt.time(self.hour, self.minute, self.second, - self.microsecond, self.tzinfo, fold=self.fold) - return _dt + return super().time() def timetuple(self): """ @@ -1594,9 +1590,7 @@ class Timestamp(_Timestamp): >>> ts.timetz() datetime.time(10, 0, tzinfo=) """ - _dt = dt.time(self.hour, self.minute, self.second, - self.microsecond, self.tzinfo) - return _dt + return super().timetz() def toordinal(self): """