Index: Lib/datetime.py =================================================================== --- Lib/datetime.py (revision 83213) +++ Lib/datetime.py (working copy) @@ -1779,24 +1779,31 @@ # 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""" @@ -1874,9 +1881,9 @@ 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 Index: Lib/test/datetimetester.py =================================================================== --- Lib/test/datetimetester.py (revision 83213) +++ Lib/test/datetimetester.py (working copy) @@ -176,7 +176,9 @@ 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)]: Index: Modules/_datetimemodule.c =================================================================== --- Modules/_datetimemodule.c (revision 83213) +++ Modules/_datetimemodule.c (working copy) @@ -783,14 +783,15 @@ 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; @@ -823,6 +824,22 @@ 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; + } + return create_timezone(offset, name); +} + /* --------------------------------------------------------------------------- * tzinfo helpers. */ @@ -5199,7 +5216,7 @@ 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; @@ -5208,7 +5225,7 @@ 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; @@ -5217,7 +5234,7 @@ 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;