Kaydet (Commit) 1bcbaab1 authored tarafından Alexander Belopolsky's avatar Alexander Belopolsky

Issue 9183: Intern UTC timezone.

üst 5bc4fa7a
...@@ -1784,24 +1784,31 @@ class timezone(tzinfo): ...@@ -1784,24 +1784,31 @@ class timezone(tzinfo):
# Sentinel value to disallow None # Sentinel value to disallow None
_Omitted = object() _Omitted = object()
def __init__(self, offset, name=_Omitted): def __new__(cls, offset, name=_Omitted):
if name is self._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 name = None
elif not isinstance(name, str): elif not isinstance(name, str):
raise TypeError("name must be a string") raise TypeError("name must be a string")
if isinstance(offset, timedelta): if not cls._minoffset <= offset <= cls._maxoffset:
if self._minoffset <= offset <= self._maxoffset: raise ValueError("offset must be a timedelta"
if (offset.microseconds != 0 or " strictly between -timedelta(hours=24) and"
offset.seconds % 60 != 0): " timedelta(hours=24).")
raise ValueError("offset must be whole" if (offset.microseconds != 0 or
" number of minutes") offset.seconds % 60 != 0):
self._offset = offset raise ValueError("offset must be a timedelta"
else: " representing a whole number of minutes")
raise ValueError("offset out of range") return cls._create(offset, name)
else:
raise TypeError("offset must be timedelta")
@classmethod
def _create(cls, offset, name=None):
self = tzinfo.__new__(cls)
self._offset = offset
self._name = name self._name = name
return self
def __getinitargs__(self): def __getinitargs__(self):
"""pickle support""" """pickle support"""
...@@ -1879,9 +1886,9 @@ class timezone(tzinfo): ...@@ -1879,9 +1886,9 @@ class timezone(tzinfo):
minutes = rest // timedelta(minutes=1) minutes = rest // timedelta(minutes=1)
return 'UTC{}{:02d}:{:02d}'.format(sign, hours, minutes) return 'UTC{}{:02d}:{:02d}'.format(sign, hours, minutes)
timezone.utc = timezone(timedelta(0)) timezone.utc = timezone._create(timedelta(0))
timezone.min = timezone(timezone._minoffset) timezone.min = timezone._create(timezone._minoffset)
timezone.max = timezone(timezone._maxoffset) timezone.max = timezone._create(timezone._maxoffset)
""" """
Some time zone algebra. For a datetime x, let Some time zone algebra. For a datetime x, let
......
...@@ -176,7 +176,9 @@ class TestTimeZone(unittest.TestCase): ...@@ -176,7 +176,9 @@ class TestTimeZone(unittest.TestCase):
def test_constructor(self): 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 # invalid offsets
for invalid in [timedelta(microseconds=1), timedelta(1, 1), for invalid in [timedelta(microseconds=1), timedelta(1, 1),
timedelta(seconds=1), timedelta(1), -timedelta(1)]: timedelta(seconds=1), timedelta(1), -timedelta(1)]:
......
...@@ -21,6 +21,9 @@ Core and Builtins ...@@ -21,6 +21,9 @@ Core and Builtins
Library 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, - Issue #7523: Add SOCK_CLOEXEC and SOCK_NONBLOCK to the socket module,
where supported by the system. Patch by Nikita Vetoshkin. where supported by the system. Patch by Nikita Vetoshkin.
......
...@@ -767,14 +767,15 @@ typedef struct ...@@ -767,14 +767,15 @@ typedef struct
PyObject *name; PyObject *name;
} PyDateTime_TimeZone; } PyDateTime_TimeZone;
PyObject *PyDateTime_TimeZone_UTC; /* The interned UTC timezone instance */
static PyObject *PyDateTime_TimeZone_UTC;
/* Create new timezone instance checking offset range. This /* Create new timezone instance checking offset range. This
function does not check the name argument. Caller must assure function does not check the name argument. Caller must assure
that offset is a timedelta instance and name is either NULL that offset is a timedelta instance and name is either NULL
or a unicode object. */ or a unicode object. */
static PyObject * static PyObject *
new_timezone(PyObject *offset, PyObject *name) create_timezone(PyObject *offset, PyObject *name)
{ {
PyDateTime_TimeZone *self; PyDateTime_TimeZone *self;
PyTypeObject *type = &PyDateTime_TimeZoneType; PyTypeObject *type = &PyDateTime_TimeZoneType;
...@@ -783,6 +784,30 @@ new_timezone(PyObject *offset, PyObject *name) ...@@ -783,6 +784,30 @@ new_timezone(PyObject *offset, PyObject *name)
assert(PyDelta_Check(offset)); assert(PyDelta_Check(offset));
assert(name == NULL || PyUnicode_Check(name)); 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) { if (GET_TD_MICROSECONDS(offset) != 0 || GET_TD_SECONDS(offset) % 60 != 0) {
PyErr_Format(PyExc_ValueError, "offset must be a timedelta" PyErr_Format(PyExc_ValueError, "offset must be a timedelta"
" representing a whole number of minutes"); " representing a whole number of minutes");
...@@ -796,15 +821,7 @@ new_timezone(PyObject *offset, PyObject *name) ...@@ -796,15 +821,7 @@ new_timezone(PyObject *offset, PyObject *name)
return NULL; return NULL;
} }
self = (PyDateTime_TimeZone *)(type->tp_alloc(type, 0)); return create_timezone(offset, name);
if (self == NULL) {
return NULL;
}
Py_INCREF(offset);
self->offset = offset;
Py_XINCREF(name);
self->name = name;
return (PyObject *)self;
} }
/* --------------------------------------------------------------------------- /* ---------------------------------------------------------------------------
...@@ -5156,7 +5173,7 @@ PyInit__datetime(void) ...@@ -5156,7 +5173,7 @@ PyInit__datetime(void)
delta = new_delta(0, 0, 0, 0); delta = new_delta(0, 0, 0, 0);
if (delta == NULL) if (delta == NULL)
return NULL; return NULL;
x = new_timezone(delta, NULL); x = create_timezone(delta, NULL);
Py_DECREF(delta); Py_DECREF(delta);
if (x == NULL || PyDict_SetItemString(d, "utc", x) < 0) if (x == NULL || PyDict_SetItemString(d, "utc", x) < 0)
return NULL; return NULL;
...@@ -5165,7 +5182,7 @@ PyInit__datetime(void) ...@@ -5165,7 +5182,7 @@ PyInit__datetime(void)
delta = new_delta(-1, 60, 0, 1); /* -23:59 */ delta = new_delta(-1, 60, 0, 1); /* -23:59 */
if (delta == NULL) if (delta == NULL)
return NULL; return NULL;
x = new_timezone(delta, NULL); x = create_timezone(delta, NULL);
Py_DECREF(delta); Py_DECREF(delta);
if (x == NULL || PyDict_SetItemString(d, "min", x) < 0) if (x == NULL || PyDict_SetItemString(d, "min", x) < 0)
return NULL; return NULL;
...@@ -5174,7 +5191,7 @@ PyInit__datetime(void) ...@@ -5174,7 +5191,7 @@ PyInit__datetime(void)
delta = new_delta(0, (23 * 60 + 59) * 60, 0, 0); /* +23:59 */ delta = new_delta(0, (23 * 60 + 59) * 60, 0, 0); /* +23:59 */
if (delta == NULL) if (delta == NULL)
return NULL; return NULL;
x = new_timezone(delta, NULL); x = create_timezone(delta, NULL);
Py_DECREF(delta); Py_DECREF(delta);
if (x == NULL || PyDict_SetItemString(d, "max", x) < 0) if (x == NULL || PyDict_SetItemString(d, "max", x) < 0)
return NULL; return NULL;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment