Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1bcbaab

Browse files
committed
Issue 9183: Intern UTC timezone.
1 parent 5bc4fa7 commit 1bcbaab

4 files changed

Lines changed: 60 additions & 31 deletions

File tree

Lib/datetime.py

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1784,24 +1784,31 @@ class timezone(tzinfo):
17841784

17851785
# Sentinel value to disallow None
17861786
_Omitted = object()
1787-
def __init__(self, offset, name=_Omitted):
1788-
if name is self._Omitted:
1787+
def __new__(cls, offset, name=_Omitted):
1788+
if not isinstance(offset, timedelta):
1789+
raise TypeError("offset must be a timedelta")
1790+
if name is cls._Omitted:
1791+
if not offset:
1792+
return cls.utc
17891793
name = None
17901794
elif not isinstance(name, str):
17911795
raise TypeError("name must be a string")
1792-
if isinstance(offset, timedelta):
1793-
if self._minoffset <= offset <= self._maxoffset:
1794-
if (offset.microseconds != 0 or
1795-
offset.seconds % 60 != 0):
1796-
raise ValueError("offset must be whole"
1797-
" number of minutes")
1798-
self._offset = offset
1799-
else:
1800-
raise ValueError("offset out of range")
1801-
else:
1802-
raise TypeError("offset must be timedelta")
1796+
if not cls._minoffset <= offset <= cls._maxoffset:
1797+
raise ValueError("offset must be a timedelta"
1798+
" strictly between -timedelta(hours=24) and"
1799+
" timedelta(hours=24).")
1800+
if (offset.microseconds != 0 or
1801+
offset.seconds % 60 != 0):
1802+
raise ValueError("offset must be a timedelta"
1803+
" representing a whole number of minutes")
1804+
return cls._create(offset, name)
18031805

1806+
@classmethod
1807+
def _create(cls, offset, name=None):
1808+
self = tzinfo.__new__(cls)
1809+
self._offset = offset
18041810
self._name = name
1811+
return self
18051812

18061813
def __getinitargs__(self):
18071814
"""pickle support"""
@@ -1879,9 +1886,9 @@ def _name_from_offset(delta):
18791886
minutes = rest // timedelta(minutes=1)
18801887
return 'UTC{}{:02d}:{:02d}'.format(sign, hours, minutes)
18811888

1882-
timezone.utc = timezone(timedelta(0))
1883-
timezone.min = timezone(timezone._minoffset)
1884-
timezone.max = timezone(timezone._maxoffset)
1889+
timezone.utc = timezone._create(timedelta(0))
1890+
timezone.min = timezone._create(timezone._minoffset)
1891+
timezone.max = timezone._create(timezone._maxoffset)
18851892

18861893
"""
18871894
Some time zone algebra. For a datetime x, let

Lib/test/datetimetester.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,9 @@ def test_class_members(self):
176176

177177

178178
def test_constructor(self):
179-
self.assertEqual(timezone.utc, timezone(timedelta(0)))
179+
self.assertIs(timezone.utc, timezone(timedelta(0)))
180+
self.assertIsNot(timezone.utc, timezone(timedelta(0), 'UTC'))
181+
self.assertEqual(timezone.utc, timezone(timedelta(0), 'UTC'))
180182
# invalid offsets
181183
for invalid in [timedelta(microseconds=1), timedelta(1, 1),
182184
timedelta(seconds=1), timedelta(1), -timedelta(1)]:

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ Core and Builtins
2121
Library
2222
-------
2323

24+
- Issue 9183: ``datetime.timezone(datetime.timedelta(0))`` will now
25+
return the same instance as ``datetime.timezone.utc``.
26+
2427
- Issue #7523: Add SOCK_CLOEXEC and SOCK_NONBLOCK to the socket module,
2528
where supported by the system. Patch by Nikita Vetoshkin.
2629

Modules/_datetimemodule.c

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -767,14 +767,15 @@ typedef struct
767767
PyObject *name;
768768
} PyDateTime_TimeZone;
769769

770-
PyObject *PyDateTime_TimeZone_UTC;
770+
/* The interned UTC timezone instance */
771+
static PyObject *PyDateTime_TimeZone_UTC;
771772

772773
/* Create new timezone instance checking offset range. This
773774
function does not check the name argument. Caller must assure
774775
that offset is a timedelta instance and name is either NULL
775776
or a unicode object. */
776777
static PyObject *
777-
new_timezone(PyObject *offset, PyObject *name)
778+
create_timezone(PyObject *offset, PyObject *name)
778779
{
779780
PyDateTime_TimeZone *self;
780781
PyTypeObject *type = &PyDateTime_TimeZoneType;
@@ -783,6 +784,30 @@ new_timezone(PyObject *offset, PyObject *name)
783784
assert(PyDelta_Check(offset));
784785
assert(name == NULL || PyUnicode_Check(name));
785786

787+
self = (PyDateTime_TimeZone *)(type->tp_alloc(type, 0));
788+
if (self == NULL) {
789+
return NULL;
790+
}
791+
Py_INCREF(offset);
792+
self->offset = offset;
793+
Py_XINCREF(name);
794+
self->name = name;
795+
return (PyObject *)self;
796+
}
797+
798+
static int delta_bool(PyDateTime_Delta *self);
799+
800+
static PyObject *
801+
new_timezone(PyObject *offset, PyObject *name)
802+
{
803+
assert(offset != NULL);
804+
assert(PyDelta_Check(offset));
805+
assert(name == NULL || PyUnicode_Check(name));
806+
807+
if (name == NULL && delta_bool((PyDateTime_Delta *)offset) == 0) {
808+
Py_INCREF(PyDateTime_TimeZone_UTC);
809+
return PyDateTime_TimeZone_UTC;
810+
}
786811
if (GET_TD_MICROSECONDS(offset) != 0 || GET_TD_SECONDS(offset) % 60 != 0) {
787812
PyErr_Format(PyExc_ValueError, "offset must be a timedelta"
788813
" representing a whole number of minutes");
@@ -796,15 +821,7 @@ new_timezone(PyObject *offset, PyObject *name)
796821
return NULL;
797822
}
798823

799-
self = (PyDateTime_TimeZone *)(type->tp_alloc(type, 0));
800-
if (self == NULL) {
801-
return NULL;
802-
}
803-
Py_INCREF(offset);
804-
self->offset = offset;
805-
Py_XINCREF(name);
806-
self->name = name;
807-
return (PyObject *)self;
824+
return create_timezone(offset, name);
808825
}
809826

810827
/* ---------------------------------------------------------------------------
@@ -5156,7 +5173,7 @@ PyInit__datetime(void)
51565173
delta = new_delta(0, 0, 0, 0);
51575174
if (delta == NULL)
51585175
return NULL;
5159-
x = new_timezone(delta, NULL);
5176+
x = create_timezone(delta, NULL);
51605177
Py_DECREF(delta);
51615178
if (x == NULL || PyDict_SetItemString(d, "utc", x) < 0)
51625179
return NULL;
@@ -5165,7 +5182,7 @@ PyInit__datetime(void)
51655182
delta = new_delta(-1, 60, 0, 1); /* -23:59 */
51665183
if (delta == NULL)
51675184
return NULL;
5168-
x = new_timezone(delta, NULL);
5185+
x = create_timezone(delta, NULL);
51695186
Py_DECREF(delta);
51705187
if (x == NULL || PyDict_SetItemString(d, "min", x) < 0)
51715188
return NULL;
@@ -5174,7 +5191,7 @@ PyInit__datetime(void)
51745191
delta = new_delta(0, (23 * 60 + 59) * 60, 0, 0); /* +23:59 */
51755192
if (delta == NULL)
51765193
return NULL;
5177-
x = new_timezone(delta, NULL);
5194+
x = create_timezone(delta, NULL);
51785195
Py_DECREF(delta);
51795196
if (x == NULL || PyDict_SetItemString(d, "max", x) < 0)
51805197
return NULL;

0 commit comments

Comments
 (0)