summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/datetime.py39
-rw-r--r--Lib/test/datetimetester.py4
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/_datetimemodule.c45
4 files changed, 60 insertions, 31 deletions
diff --git a/Lib/datetime.py b/Lib/datetime.py
index d640f75..29ffe24 100644
--- a/Lib/datetime.py
+++ b/Lib/datetime.py
@@ -1784,24 +1784,31 @@ class timezone(tzinfo):
# Sentinel value to disallow None
_Omitted = object()
- def __init__(self, offset, name=_Omitted):
- if name is self._Omitted:
+ def __new__(cls, offset, name=_Omitted):
+ if not isinstance(offset, timedelta):
+ raise TypeError("offset must be a timedelta")
+ if name is cls._Omitted:
+ if not offset:
+ return cls.utc
name = None
elif not isinstance(name, str):
raise TypeError("name must be a string")
- if isinstance(offset, timedelta):
- if self._minoffset <= offset <= self._maxoffset:
- if (offset.microseconds != 0 or
- offset.seconds % 60 != 0):
- raise ValueError("offset must be whole"
- " number of minutes")
- self._offset = offset
- else:
- raise ValueError("offset out of range")
- else:
- raise TypeError("offset must be timedelta")
+ if not cls._minoffset <= offset <= cls._maxoffset:
+ raise ValueError("offset must be a timedelta"
+ " strictly between -timedelta(hours=24) and"
+ " timedelta(hours=24).")
+ if (offset.microseconds != 0 or
+ offset.seconds % 60 != 0):
+ raise ValueError("offset must be a timedelta"
+ " representing a whole number of minutes")
+ return cls._create(offset, name)
+ @classmethod
+ def _create(cls, offset, name=None):
+ self = tzinfo.__new__(cls)
+ self._offset = offset
self._name = name
+ return self
def __getinitargs__(self):
"""pickle support"""
@@ -1879,9 +1886,9 @@ class timezone(tzinfo):
minutes = rest // timedelta(minutes=1)
return 'UTC{}{:02d}:{:02d}'.format(sign, hours, minutes)
-timezone.utc = timezone(timedelta(0))
-timezone.min = timezone(timezone._minoffset)
-timezone.max = timezone(timezone._maxoffset)
+timezone.utc = timezone._create(timedelta(0))
+timezone.min = timezone._create(timezone._minoffset)
+timezone.max = timezone._create(timezone._maxoffset)
"""
Some time zone algebra. For a datetime x, let
diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py
index 0ee544b..e91eb67 100644
--- a/Lib/test/datetimetester.py
+++ b/Lib/test/datetimetester.py
@@ -176,7 +176,9 @@ class TestTimeZone(unittest.TestCase):
def test_constructor(self):
- self.assertEqual(timezone.utc, timezone(timedelta(0)))
+ self.assertIs(timezone.utc, timezone(timedelta(0)))
+ self.assertIsNot(timezone.utc, timezone(timedelta(0), 'UTC'))
+ self.assertEqual(timezone.utc, timezone(timedelta(0), 'UTC'))
# invalid offsets
for invalid in [timedelta(microseconds=1), timedelta(1, 1),
timedelta(seconds=1), timedelta(1), -timedelta(1)]:
diff --git a/Misc/NEWS b/Misc/NEWS
index 7fb305c..c09e24f 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -21,6 +21,9 @@ Core and Builtins
Library
-------
+- Issue 9183: ``datetime.timezone(datetime.timedelta(0))`` will now
+ return the same instance as ``datetime.timezone.utc``.
+
- Issue #7523: Add SOCK_CLOEXEC and SOCK_NONBLOCK to the socket module,
where supported by the system. Patch by Nikita Vetoshkin.
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c
index 8c47351..9a04549 100644
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -767,14 +767,15 @@ typedef struct
PyObject *name;
} PyDateTime_TimeZone;
-PyObject *PyDateTime_TimeZone_UTC;
+/* The interned UTC timezone instance */
+static PyObject *PyDateTime_TimeZone_UTC;
/* Create new timezone instance checking offset range. This
function does not check the name argument. Caller must assure
that offset is a timedelta instance and name is either NULL
or a unicode object. */
static PyObject *
-new_timezone(PyObject *offset, PyObject *name)
+create_timezone(PyObject *offset, PyObject *name)
{
PyDateTime_TimeZone *self;
PyTypeObject *type = &PyDateTime_TimeZoneType;
@@ -783,6 +784,30 @@ new_timezone(PyObject *offset, PyObject *name)
assert(PyDelta_Check(offset));
assert(name == NULL || PyUnicode_Check(name));
+ self = (PyDateTime_TimeZone *)(type->tp_alloc(type, 0));
+ if (self == NULL) {
+ return NULL;
+ }
+ Py_INCREF(offset);
+ self->offset = offset;
+ Py_XINCREF(name);
+ self->name = name;
+ return (PyObject *)self;
+}
+
+static int delta_bool(PyDateTime_Delta *self);
+
+static PyObject *
+new_timezone(PyObject *offset, PyObject *name)
+{
+ assert(offset != NULL);
+ assert(PyDelta_Check(offset));
+ assert(name == NULL || PyUnicode_Check(name));
+
+ if (name == NULL && delta_bool((PyDateTime_Delta *)offset) == 0) {
+ Py_INCREF(PyDateTime_TimeZone_UTC);
+ return PyDateTime_TimeZone_UTC;
+ }
if (GET_TD_MICROSECONDS(offset) != 0 || GET_TD_SECONDS(offset) % 60 != 0) {
PyErr_Format(PyExc_ValueError, "offset must be a timedelta"
" representing a whole number of minutes");
@@ -796,15 +821,7 @@ new_timezone(PyObject *offset, PyObject *name)
return NULL;
}
- self = (PyDateTime_TimeZone *)(type->tp_alloc(type, 0));
- if (self == NULL) {
- return NULL;
- }
- Py_INCREF(offset);
- self->offset = offset;
- Py_XINCREF(name);
- self->name = name;
- return (PyObject *)self;
+ return create_timezone(offset, name);
}
/* ---------------------------------------------------------------------------
@@ -5156,7 +5173,7 @@ PyInit__datetime(void)
delta = new_delta(0, 0, 0, 0);
if (delta == NULL)
return NULL;
- x = new_timezone(delta, NULL);
+ x = create_timezone(delta, NULL);
Py_DECREF(delta);
if (x == NULL || PyDict_SetItemString(d, "utc", x) < 0)
return NULL;
@@ -5165,7 +5182,7 @@ PyInit__datetime(void)
delta = new_delta(-1, 60, 0, 1); /* -23:59 */
if (delta == NULL)
return NULL;
- x = new_timezone(delta, NULL);
+ x = create_timezone(delta, NULL);
Py_DECREF(delta);
if (x == NULL || PyDict_SetItemString(d, "min", x) < 0)
return NULL;
@@ -5174,7 +5191,7 @@ PyInit__datetime(void)
delta = new_delta(0, (23 * 60 + 59) * 60, 0, 0); /* +23:59 */
if (delta == NULL)
return NULL;
- x = new_timezone(delta, NULL);
+ x = create_timezone(delta, NULL);
Py_DECREF(delta);
if (x == NULL || PyDict_SetItemString(d, "max", x) < 0)
return NULL;