From 2e4dd336e5b50fd30947fdecb605ddcd71f7f6f5 Mon Sep 17 00:00:00 2001 From: Zackery Spytz Date: Wed, 23 Sep 2020 12:43:45 -0600 Subject: bpo-30155: Add macros to get tzinfo from datetime instances (GH-21633) Add PyDateTime_DATE_GET_TZINFO() and PyDateTime_TIME_GET_TZINFO() macros. --- Doc/c-api/datetime.rst | 11 +++++++++++ Doc/whatsnew/3.10.rst | 6 ++++++ Include/datetime.h | 8 ++++++++ Lib/test/datetimetester.py | 14 ++++++++++---- .../next/C API/2020-07-26-19-39-45.bpo-30155.rHZRJ_.rst | 3 +++ Modules/_datetimemodule.c | 11 +++-------- Modules/_testcapimodule.c | 6 ++++-- Modules/_zoneinfo.c | 4 +--- 8 files changed, 46 insertions(+), 17 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2020-07-26-19-39-45.bpo-30155.rHZRJ_.rst diff --git a/Doc/c-api/datetime.rst b/Doc/c-api/datetime.rst index bd4f1ff..415ce4c 100644 --- a/Doc/c-api/datetime.rst +++ b/Doc/c-api/datetime.rst @@ -185,6 +185,11 @@ must not be ``NULL``, and the type is not checked: Return the microsecond, as an int from 0 through 999999. +.. c:function:: PyObject* PyDateTime_DATE_GET_TZINFO(PyDateTime_DateTime *o) + + Return the tzinfo (which may be ``None``). + + .. versionadded:: 3.10 Macros to extract fields from time objects. The argument must be an instance of :c:data:`PyDateTime_Time`, including subclasses. The argument must not be ``NULL``, @@ -209,6 +214,12 @@ and the type is not checked: Return the microsecond, as an int from 0 through 999999. +.. c:function:: PyObject* PyDateTime_TIME_GET_TZINFO(PyDateTime_Time *o) + + Return the tzinfo (which may be ``None``). + + .. versionadded:: 3.10 + Macros to extract fields from time delta objects. The argument must be an instance of :c:data:`PyDateTime_Delta`, including subclasses. The argument must diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 606fad2..1228f26 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -231,6 +231,12 @@ New Features Python executable. (Contributed by Victor Stinner in :issue:`23427`.) +* The :c:func:`PyDateTime_DATE_GET_TZINFO` and + :c:func:`PyDateTime_TIME_GET_TZINFO` macros have been added for accessing + the ``tzinfo`` attributes of :class:`datetime.datetime` and + :class:`datetime.time` objects. + (Contributed by Zackery Spytz in :issue:`30155`.) + Porting to Python 3.10 ---------------------- diff --git a/Include/datetime.h b/Include/datetime.h index 5d9f255..bb56520 100644 --- a/Include/datetime.h +++ b/Include/datetime.h @@ -115,6 +115,10 @@ typedef struct /* Apply for date and datetime instances. */ + +// o is a pointer to a time or a datetime object. +#define _PyDateTime_HAS_TZINFO(o) (((_PyDateTime_BaseTZInfo *)(o))->hastzinfo) + #define PyDateTime_GET_YEAR(o) ((((PyDateTime_Date*)o)->data[0] << 8) | \ ((PyDateTime_Date*)o)->data[1]) #define PyDateTime_GET_MONTH(o) (((PyDateTime_Date*)o)->data[2]) @@ -128,6 +132,8 @@ typedef struct (((PyDateTime_DateTime*)o)->data[8] << 8) | \ ((PyDateTime_DateTime*)o)->data[9]) #define PyDateTime_DATE_GET_FOLD(o) (((PyDateTime_DateTime*)o)->fold) +#define PyDateTime_DATE_GET_TZINFO(o) (_PyDateTime_HAS_TZINFO(o) ? \ + ((PyDateTime_DateTime *)(o))->tzinfo : Py_None) /* Apply for time instances. */ #define PyDateTime_TIME_GET_HOUR(o) (((PyDateTime_Time*)o)->data[0]) @@ -138,6 +144,8 @@ typedef struct (((PyDateTime_Time*)o)->data[4] << 8) | \ ((PyDateTime_Time*)o)->data[5]) #define PyDateTime_TIME_GET_FOLD(o) (((PyDateTime_Time*)o)->fold) +#define PyDateTime_TIME_GET_TZINFO(o) (_PyDateTime_HAS_TZINFO(o) ? \ + ((PyDateTime_Time *)(o))->tzinfo : Py_None) /* Apply for time delta instances */ #define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 520a51d..8b61c26 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -5991,30 +5991,36 @@ class CapiTest(unittest.TestCase): for klass in [datetime, DateTimeSubclass]: for args in [(1993, 8, 26, 22, 12, 55, 99999), - (1993, 8, 26, 22, 12, 55, 99999)]: + (1993, 8, 26, 22, 12, 55, 99999, + timezone.utc)]: d = klass(*args) with self.subTest(cls=klass, date=args): - hour, minute, second, microsecond = _testcapi.PyDateTime_DATE_GET(d) + hour, minute, second, microsecond, tzinfo = \ + _testcapi.PyDateTime_DATE_GET(d) self.assertEqual(hour, d.hour) self.assertEqual(minute, d.minute) self.assertEqual(second, d.second) self.assertEqual(microsecond, d.microsecond) + self.assertIs(tzinfo, d.tzinfo) def test_PyDateTime_TIME_GET(self): class TimeSubclass(time): pass for klass in [time, TimeSubclass]: - for args in [(12, 30, 20, 10), (12, 30, 20, 10)]: + for args in [(12, 30, 20, 10), + (12, 30, 20, 10, timezone.utc)]: d = klass(*args) with self.subTest(cls=klass, date=args): - hour, minute, second, microsecond = _testcapi.PyDateTime_TIME_GET(d) + hour, minute, second, microsecond, tzinfo = \ + _testcapi.PyDateTime_TIME_GET(d) self.assertEqual(hour, d.hour) self.assertEqual(minute, d.minute) self.assertEqual(second, d.second) self.assertEqual(microsecond, d.microsecond) + self.assertIs(tzinfo, d.tzinfo) def test_timezones_offset_zero(self): utc0, utc1, non_utc = _testcapi.get_timezones_offset_zero() diff --git a/Misc/NEWS.d/next/C API/2020-07-26-19-39-45.bpo-30155.rHZRJ_.rst b/Misc/NEWS.d/next/C API/2020-07-26-19-39-45.bpo-30155.rHZRJ_.rst new file mode 100644 index 0000000..a276759 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-07-26-19-39-45.bpo-30155.rHZRJ_.rst @@ -0,0 +1,3 @@ +Add :c:func:`PyDateTime_DATE_GET_TZINFO` and +:c:func:`PyDateTime_TIME_GET_TZINFO` macros for accessing the ``tzinfo`` +attributes of :class:`datetime.datetime` and :class:`datetime.time` objects. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 74a54e7..0631272 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -115,14 +115,9 @@ class datetime.IsoCalendarDate "PyDateTime_IsoCalendarDate *" "&PyDateTime_IsoCa #define SET_TD_SECONDS(o, v) ((o)->seconds = (v)) #define SET_TD_MICROSECONDS(o, v) ((o)->microseconds = (v)) -/* p is a pointer to a time or a datetime object; HASTZINFO(p) returns - * p->hastzinfo. - */ -#define HASTZINFO(p) (((_PyDateTime_BaseTZInfo *)(p))->hastzinfo) -#define GET_TIME_TZINFO(p) (HASTZINFO(p) ? \ - ((PyDateTime_Time *)(p))->tzinfo : Py_None) -#define GET_DT_TZINFO(p) (HASTZINFO(p) ? \ - ((PyDateTime_DateTime *)(p))->tzinfo : Py_None) +#define HASTZINFO _PyDateTime_HAS_TZINFO +#define GET_TIME_TZINFO PyDateTime_TIME_GET_TZINFO +#define GET_DT_TZINFO PyDateTime_DATE_GET_TZINFO /* M is a char or int claiming to be a valid month. The macro is equivalent * to the two-sided Python test * 1 <= M <= 12 diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 7536d29..0e09877 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2677,8 +2677,9 @@ test_PyDateTime_DATE_GET(PyObject *self, PyObject *obj) minute = PyDateTime_DATE_GET_MINUTE(obj); second = PyDateTime_DATE_GET_SECOND(obj); microsecond = PyDateTime_DATE_GET_MICROSECOND(obj); + PyObject *tzinfo = PyDateTime_DATE_GET_TZINFO(obj); - return Py_BuildValue("(llll)", hour, minute, second, microsecond); + return Py_BuildValue("(llllO)", hour, minute, second, microsecond, tzinfo); } static PyObject * @@ -2690,8 +2691,9 @@ test_PyDateTime_TIME_GET(PyObject *self, PyObject *obj) minute = PyDateTime_TIME_GET_MINUTE(obj); second = PyDateTime_TIME_GET_SECOND(obj); microsecond = PyDateTime_TIME_GET_MICROSECOND(obj); + PyObject *tzinfo = PyDateTime_TIME_GET_TZINFO(obj); - return Py_BuildValue("(llll)", hour, minute, second, microsecond); + return Py_BuildValue("(llllO)", hour, minute, second, microsecond, tzinfo); } static PyObject * diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index 2cee65f..bee59b8 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -484,9 +484,7 @@ zoneinfo_tzname(PyObject *self, PyObject *dt) return tti->tzname; } -#define HASTZINFO(p) (((_PyDateTime_BaseTZInfo *)(p))->hastzinfo) -#define GET_DT_TZINFO(p) \ - (HASTZINFO(p) ? ((PyDateTime_DateTime *)(p))->tzinfo : Py_None) +#define GET_DT_TZINFO PyDateTime_DATE_GET_TZINFO static PyObject * zoneinfo_fromutc(PyObject *obj_self, PyObject *dt) -- cgit v0.12