diff --git a/ci/code_checks.sh b/ci/code_checks.sh index f63cc1fcc5767..965a5ae5cbd50 100755 --- a/ci/code_checks.sh +++ b/ci/code_checks.sh @@ -105,10 +105,6 @@ if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then pandas.errors.UnsupportedFunctionCall \ pandas.test \ pandas.NaT \ - pandas.Timestamp.date \ - pandas.Timestamp.dst \ - pandas.Timestamp.isocalendar \ - pandas.Timestamp.isoweekday \ pandas.Timestamp.strptime \ pandas.Timestamp.time \ pandas.Timestamp.timetuple \ diff --git a/doc/source/whatsnew/v2.1.0.rst b/doc/source/whatsnew/v2.1.0.rst index d9cdc8beaebea..b9b2dbb18565c 100644 --- a/doc/source/whatsnew/v2.1.0.rst +++ b/doc/source/whatsnew/v2.1.0.rst @@ -354,6 +354,7 @@ 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.date`, :meth:`Timestamp.isocalendar` were returning incorrect results for inputs outside those supported by the Python standard library's datetime module (:issue:`53668`) - Bug in :meth:`Timestamp.round` with values close to the implementation bounds returning incorrect results instead of raising ``OutOfBoundsDatetime`` (:issue:`51494`) - 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`) diff --git a/pandas/_libs/tslibs/nattype.pyx b/pandas/_libs/tslibs/nattype.pyx index 75205a359db68..a179aa225f845 100644 --- a/pandas/_libs/tslibs/nattype.pyx +++ b/pandas/_libs/tslibs/nattype.pyx @@ -437,6 +437,14 @@ class NaTType(_NaT): Return the day of the week represented by the date. Monday == 1 ... Sunday == 7. + + Examples + -------- + >>> ts = pd.Timestamp('2023-01-01 10:00:00') + >>> ts + Timestamp('2023-01-01 10:00:00') + >>> ts.isoweekday() + 7 """, ) total_seconds = _make_nan_func( @@ -506,13 +514,9 @@ class NaTType(_NaT): """, ) # _nat_methods - 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) @@ -524,6 +528,48 @@ class NaTType(_NaT): # ---------------------------------------------------------------------- # The remaining methods have docstrings copy/pasted from the analogous # Timestamp methods. + isocalendar = _make_error_func( + "isocalendar", + """ + Return a named tuple containing ISO year, week number, and weekday. + + Examples + -------- + >>> ts = pd.Timestamp('2023-01-01 10:00:00') + >>> ts + Timestamp('2023-01-01 10:00:00') + >>> ts.isocalendar() + datetime.IsoCalendarDate(year=2022, week=52, weekday=7) + """ + ) + dst = _make_error_func( + "dst", + """ + Return the daylight saving time (DST) adjustment. + + Examples + -------- + >>> ts = pd.Timestamp('2000-06-01 00:00:00', tz='Europe/Brussels') + >>> ts + Timestamp('2000-06-01 00:00:00+0200', tz='Europe/Brussels') + >>> ts.dst() + datetime.timedelta(seconds=3600) + """ + ) + date = _make_nat_func( + "date", + """ + Return date object with same year, month and day. + + Examples + -------- + >>> ts = pd.Timestamp('2023-01-01 10:00:00.00') + >>> ts + Timestamp('2023-01-01 10:00:00') + >>> ts.date() + datetime.date(2023, 1, 1) + """ + ) ctime = _make_error_func( "ctime", diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index d66e856ef77cf..54f0dac459990 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 @@ -1531,6 +1533,64 @@ class Timestamp(_Timestamp): ) from err return _dt.ctime() + def date(self): + """ + Return date object with same year, month and day. + + Examples + -------- + >>> ts = pd.Timestamp('2023-01-01 10:00:00.00') + >>> ts + Timestamp('2023-01-01 10:00:00') + >>> ts.date() + datetime.date(2023, 1, 1) + """ + try: + _dt = dt.date(self.year, self.month, self.day) + except ValueError as err: + raise NotImplementedError( + "date not yet supported on Timestamps which " + "are outside the range of Python's standard library. " + ) from err + return _dt + + def dst(self): + """ + Return the daylight saving time (DST) adjustment. + + Examples + -------- + >>> ts = pd.Timestamp('2000-06-01 00:00:00', tz='Europe/Brussels') + >>> ts + Timestamp('2000-06-01 00:00:00+0200', tz='Europe/Brussels') + >>> ts.dst() + datetime.timedelta(seconds=3600) + """ + return super().dst() + + def isocalendar(self): + """ + Return a named tuple containing ISO year, week number, and weekday. + + Examples + -------- + >>> ts = pd.Timestamp('2023-01-01 10:00:00') + >>> ts + Timestamp('2023-01-01 10:00:00') + >>> ts.isocalendar() + datetime.IsoCalendarDate(year=2022, week=52, weekday=7) + """ + 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( + "isocalendar not yet supported on Timestamps which " + "are outside the range of Python's standard library. " + ) from err + return _dt.isocalendar() + # Issue 25016. @classmethod def strptime(cls, date_string, format): @@ -2384,9 +2444,18 @@ default 'raise' Return the day of the week represented by the date. Monday == 1 ... Sunday == 7. + + Examples + -------- + >>> ts = pd.Timestamp('2023-01-01 10:00:00') + >>> ts + Timestamp('2023-01-01 10:00:00') + >>> ts.isoweekday() + 7 """ # same as super().isoweekday(), but that breaks because of how # we have overridden year, see note in create_timestamp_from_ts + return self.weekday() + 1 def weekday(self): diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index afb4dd7422114..2e4b41e06bbd6 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -1124,9 +1124,21 @@ 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 = "^date" + with pytest.raises(NotImplementedError, match=func + msg): + ts.date() + func = "^isocalendar" + with pytest.raises(NotImplementedError, match=func + msg): + ts.isocalendar()