From efc5790e0b3390cd25155420bf047346018856a8 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Thu, 13 Mar 2025 20:24:35 +0000 Subject: [PATCH 1/3] Inital --- Doc/library/datetime.rst | 4 ++++ Lib/_pydatetime.py | 7 +++++++ Lib/test/datetimetester.py | 7 +++++++ ...03-13-20-21-00.gh-issue-115810.qwrb231.rst | 2 ++ Modules/_datetimemodule.c | 19 ++++++++++++++++++- 5 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-03-13-20-21-00.gh-issue-115810.qwrb231.rst diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index 1af7d6be750102..83417b98223d9a 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -2421,6 +2421,10 @@ Class attributes: The UTC time zone, ``timezone(timedelta(0))``. +.. attribute:: timezone.name + + The name of the timezone if specified during creation. If no name is provided, + an empty string is returned. .. index:: single: % (percent); datetime format diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index 26bcd1e491d78c..c3bc24c522064a 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -2486,6 +2486,13 @@ def tzname(self, dt): raise TypeError("tzname() argument must be a datetime instance" " or None") + @property + def name(self): + """Return provided name otherwise empty string.""" + if not self._name: + return '' + return self._name + def dst(self, dt): if isinstance(dt, datetime) or dt is None: return None diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 84eb872f964ba1..7bd424662bb9d6 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -348,6 +348,13 @@ def test_tzname(self): with self.assertRaises(TypeError): self.EST.tzname('') with self.assertRaises(TypeError): self.EST.tzname(5) + def test_name_property(self): + tz_named = timezone(timedelta(hours=5), 'IST') + self.assertEqual(tz_named.name, 'IST') + + tz_unnamed = timezone(timedelta(hours=0)) + self.assertEqual(tz_unnamed.name, '') + def test_fromutc(self): with self.assertRaises(ValueError): timezone.utc.fromutc(self.DT) diff --git a/Misc/NEWS.d/next/Library/2025-03-13-20-21-00.gh-issue-115810.qwrb231.rst b/Misc/NEWS.d/next/Library/2025-03-13-20-21-00.gh-issue-115810.qwrb231.rst new file mode 100644 index 00000000000000..a5a0676b69e9bb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-03-13-20-21-00.gh-issue-115810.qwrb231.rst @@ -0,0 +1,2 @@ +Add ``name`` attribute to :class:`datetime.timezone` that returns the ``name`` +provided during creation else an empty string. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 83b92fe606984c..8aff498aebef74 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -4373,6 +4373,16 @@ timezone_tzname(PyObject *op, PyObject *dt) return timezone_str(op); } +static PyObject * +timezone_name(PyDateTime_TimeZone *self, void *closure) +{ + if (self->name == NULL) { + return PyUnicode_FromString(""); + } + Py_INCREF(self->name); + return self->name; +} + static PyObject * timezone_utcoffset(PyObject *op, PyObject *dt) { @@ -4440,6 +4450,13 @@ static PyMethodDef timezone_methods[] = { {NULL, NULL} }; +static PyGetSetDef timezone_getset[] = { + {"name", (getter)timezone_name, NULL, + PyDoc_STR("If name is specified when timezone is created, returns the name."), NULL}, + {NULL} +}; + + static const char timezone_doc[] = PyDoc_STR("Fixed offset from UTC implementation of tzinfo."); @@ -4473,7 +4490,7 @@ static PyTypeObject PyDateTime_TimeZoneType = { 0, /* tp_iternext */ timezone_methods, /* tp_methods */ 0, /* tp_members */ - 0, /* tp_getset */ + timezone_getset, /* tp_getset */ 0, /* tp_base; filled in PyInit__datetime */ 0, /* tp_dict */ 0, /* tp_descr_get */ From cff696369b00332dba2caf4f95480d3d657b12c1 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Fri, 23 Oct 2026 19:13:55 +0100 Subject: [PATCH 2/3] Update implementation --- Doc/library/datetime.rst | 9 +++---- Doc/whatsnew/3.16.rst | 4 ++++ Lib/_pydatetime.py | 4 +--- Lib/test/datetimetester.py | 13 ++++++---- ...03-13-20-21-00.gh-issue-115810.qwrb231.rst | 4 ++-- Modules/_datetimemodule.c | 24 +++++++++---------- 6 files changed, 32 insertions(+), 26 deletions(-) diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index 24a89d3f43a8aa..691a1e127ad2b3 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -2476,6 +2476,11 @@ where historical changes have been made to civil time. Return ``dt + offset``. The *dt* argument must be an aware :class:`.datetime` instance, with ``tzinfo`` set to ``self``. +.. attribute:: timezone.name + + The *name* passed to the constructor, or ``None`` if no name was passed. + + .. versionadded:: next Class attributes: @@ -2483,10 +2488,6 @@ Class attributes: The UTC time zone, ``timezone(timedelta(0))``. -.. attribute:: timezone.name - - The name of the timezone if specified during creation. If no name is provided, - an empty string is returned. .. index:: single: % (percent); datetime format diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index f8fb9bf4d47e7b..5e1821dbc8d2cd 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -392,6 +392,10 @@ datetime :meth:`datetime.time.strptime`. (Contributed by Stan Ulbrych and Karoline Pauls in :gh:`157290`.) +* Add :attr:`datetime.timezone.name`, the *name* passed to the constructor, + or ``None`` if no name was passed. + (Contributed by Stan Ulbrych in :gh:`131210`.) + dbm.gnu ------- diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index c6c2f5ee0a4ad4..076183139b28ae 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -2541,9 +2541,7 @@ def tzname(self, dt): @property def name(self): - """Return provided name otherwise empty string.""" - if not self._name: - return '' + """Name given when the timezone was created.""" return self._name def dst(self, dt): diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 55e51eaa5f7cca..37491f5e7c37eb 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -354,12 +354,15 @@ def test_tzname(self): with self.assertRaises(TypeError): self.EST.tzname('') with self.assertRaises(TypeError): self.EST.tzname(5) - def test_name_property(self): - tz_named = timezone(timedelta(hours=5), 'IST') - self.assertEqual(tz_named.name, 'IST') + def test_name(self): + self.assertEqual('IST', timezone(5 * HOUR, 'IST').name) + self.assertEqual('\ud800', timezone(ZERO, '\ud800').name) - tz_unnamed = timezone(timedelta(hours=0)) - self.assertEqual(tz_unnamed.name, '') + self.assertIsNone(UTC.name) + self.assertIsNone(timezone(ZERO).name) + self.assertIsNone(timezone(-5 * HOUR).name) + + with self.assertRaises(AttributeError): self.EST.name = 'EST' def test_fromutc(self): with self.assertRaises(ValueError): diff --git a/Misc/NEWS.d/next/Library/2025-03-13-20-21-00.gh-issue-115810.qwrb231.rst b/Misc/NEWS.d/next/Library/2025-03-13-20-21-00.gh-issue-115810.qwrb231.rst index a5a0676b69e9bb..253f4a5de95738 100644 --- a/Misc/NEWS.d/next/Library/2025-03-13-20-21-00.gh-issue-115810.qwrb231.rst +++ b/Misc/NEWS.d/next/Library/2025-03-13-20-21-00.gh-issue-115810.qwrb231.rst @@ -1,2 +1,2 @@ -Add ``name`` attribute to :class:`datetime.timezone` that returns the ``name`` -provided during creation else an empty string. +Add :attr:`datetime.timezone.name`, the *name* passed to the constructor, +or ``None`` if no name was passed. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 5190461408d7a7..959abcf5e421cf 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -4491,16 +4491,6 @@ timezone_tzname(PyObject *op, PyObject *dt) return timezone_str(op); } -static PyObject * -timezone_name(PyDateTime_TimeZone *self, void *closure) -{ - if (self->name == NULL) { - return PyUnicode_FromString(""); - } - Py_INCREF(self->name); - return self->name; -} - static PyObject * timezone_utcoffset(PyObject *op, PyObject *dt) { @@ -4568,9 +4558,19 @@ static PyMethodDef timezone_methods[] = { {NULL, NULL} }; +static PyObject * +timezone_name(PyObject *op, void *Py_UNUSED(closure)) +{ + PyDateTime_TimeZone *self = PyTimeZone_CAST(op); + if (self->name == NULL) { + Py_RETURN_NONE; + } + return Py_NewRef(self->name); +} + static PyGetSetDef timezone_getset[] = { - {"name", (getter)timezone_name, NULL, - PyDoc_STR("If name is specified when timezone is created, returns the name."), NULL}, + {"name", timezone_name, NULL, + PyDoc_STR("Name given when the timezone was created.")}, {NULL} }; From f278520e2f49479d68959b24109953217d53c0ed Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Fri, 23 Oct 2026 19:32:23 +0100 Subject: [PATCH 3/3] Use subtests --- Lib/test/datetimetester.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 37491f5e7c37eb..53285d918c17ca 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -355,12 +355,13 @@ def test_tzname(self): with self.assertRaises(TypeError): self.EST.tzname(5) def test_name(self): - self.assertEqual('IST', timezone(5 * HOUR, 'IST').name) - self.assertEqual('\ud800', timezone(ZERO, '\ud800').name) + for offset, name in ((5 * HOUR, 'IST'), (ZERO, '\ud800')): + with self.subTest(offset=offset, name=name): + self.assertEqual(name, timezone(offset, name).name) - self.assertIsNone(UTC.name) - self.assertIsNone(timezone(ZERO).name) - self.assertIsNone(timezone(-5 * HOUR).name) + for tz in (UTC, timezone(ZERO), timezone(-5 * HOUR)): + with self.subTest(tz=tz): + self.assertIsNone(tz.name) with self.assertRaises(AttributeError): self.EST.name = 'EST'