Index: Include/datetime.h =================================================================== --- Include/datetime.h (revision 69265) +++ Include/datetime.h (working copy) @@ -45,7 +45,12 @@ PyObject_HEAD /* a pure abstract base clase */ } PyDateTime_TZInfo; +typedef struct +{ + PyDateTime_TZInfo tzinfo; /* Inherit from tzinfo */ +} PyDateTime_UTC; + /* The datetime and time types have hashcodes, and an optional tzinfo member, * present if and only if hastzinfo is true. */ @@ -144,6 +149,7 @@ PyTypeObject *TimeType; PyTypeObject *DeltaType; PyTypeObject *TZInfoType; + PyTypeObject *UTCType; /* constructors */ PyObject *(*Date_FromDate)(int, int, int, PyTypeObject*); Index: Doc/library/datetime.rst =================================================================== --- Doc/library/datetime.rst (revision 69265) +++ Doc/library/datetime.rst (working copy) @@ -30,11 +30,12 @@ have an optional time zone information member, :attr:`tzinfo`, that can contain an instance of a subclass of the abstract :class:`tzinfo` class. These :class:`tzinfo` objects capture information about the offset from UTC time, the -time zone name, and whether Daylight Saving Time is in effect. Note that no -concrete :class:`tzinfo` classes are supplied by the :mod:`datetime` module. -Supporting timezones at whatever level of detail is required is up to the -application. The rules for time adjustment across the world are more political -than rational, and there is no standard suitable for every application. +time zone name, and whether Daylight Saving Time is in effect. Note that only +one concrete :class:`tzinfo` class, the :class:`UTC` class, is supplied by the +:mod:`datetime` module. Supporting timezones at whatever level of detail is +required is up to the application. The rules for time adjustment across the +world are more political than rational, change frequently, and there is no +standard suitable for every application aside from UTC. The :mod:`datetime` module exports the following constants: @@ -99,6 +100,14 @@ time adjustment (for example, to account for time zone and/or daylight saving time). +.. class:: UTC + + A class that implements the :class:`tzinfo` abstract base class for + the UTC timezone. + + .. versionadded:: 2.7 + + Objects of these types are immutable. Objects of the :class:`date` type are always naive. @@ -116,6 +125,7 @@ object timedelta tzinfo + UTC time date datetime @@ -612,12 +622,16 @@ See also :meth:`today`, :meth:`utcnow`. -.. method:: datetime.utcnow() +.. method:: datetime.utcnow([tz_aware]) - Return the current UTC date and time, with :attr:`tzinfo` ``None``. This is like - :meth:`now`, but returns the current UTC date and time, as a naive + Return the current UTC date and time. If *tz_aware* is true, :attr:`tzinfo` is + set to an instance of :class:`UTC`, otherwise :attr:`tzinfo` is set to ``None``. + The default value for *tz_aware* is ``False``. This is like :meth:`now`, but + returns the current UTC date and time, as a :class:`datetime` object. See also :meth:`now`. + .. versionchanged:: 2.7 + Added *tz_aware* parameter. .. method:: datetime.fromtimestamp(timestamp[, tz]) @@ -1473,9 +1487,9 @@ standard local time. Applications that can't bear such ambiguities should avoid using hybrid -:class:`tzinfo` subclasses; there are no ambiguities when using UTC, or any -other fixed-offset :class:`tzinfo` subclass (such as a class representing only -EST (fixed offset -5 hours), or only EDT (fixed offset -4 hours)). +:class:`tzinfo` subclasses; there are no ambiguities when using :class:`UTC`, +or any other fixed-offset :class:`tzinfo` subclass (such as a class representing +only EST (fixed offset -5 hours), or only EDT (fixed offset -4 hours)). .. _strftime-behavior: Index: Doc/includes/tzinfo-examples.py =================================================================== --- Doc/includes/tzinfo-examples.py (revision 69265) +++ Doc/includes/tzinfo-examples.py (working copy) @@ -3,22 +3,6 @@ ZERO = timedelta(0) HOUR = timedelta(hours=1) -# A UTC class. - -class UTC(tzinfo): - """UTC""" - - def utcoffset(self, dt): - return ZERO - - def tzname(self, dt): - return "UTC" - - def dst(self, dt): - return ZERO - -utc = UTC() - # A class building tzinfo objects for fixed-offset time zones. # Note that FixedOffset(0, "UTC") is a different way to build a # UTC tzinfo object. Index: Lib/test/test_datetime.py =================================================================== --- Lib/test/test_datetime.py (revision 69265) +++ Lib/test/test_datetime.py (working copy) @@ -14,6 +14,7 @@ from datetime import timedelta from datetime import tzinfo from datetime import time +from datetime import UTC from datetime import date, datetime pickle_choices = [(pickler, unpickler, proto) @@ -41,6 +42,7 @@ # tzinfo tests class FixedOffset(tzinfo): + def __init__(self, offset, name, dstoffset=42): if isinstance(offset, int): offset = timedelta(minutes=offset) @@ -59,6 +61,7 @@ return self.__dstoffset class PicklableFixedOffset(FixedOffset): + def __init__(self, offset=None, name=None, dstoffset=None): FixedOffset.__init__(self, offset, name, dstoffset) @@ -123,11 +126,29 @@ self.assertEqual(derived.utcoffset(None), offset) self.assertEqual(derived.tzname(None), 'cookie') +class TestUTC(unittest.TestCase): + + def test_inheritance(self): + self.assertTrue(isinstance(UTC(), tzinfo)) + + def test_utcoffset(self): + self.assertEquals(timedelta(0), UTC().utcoffset(None)) + self.assertEquals(timedelta(0), UTC().utcoffset(datetime(2009, 1, 1))) + + def test_dst(self): + self.assertEquals(timedelta(0), UTC().dst(None)) + self.assertEquals(timedelta(0), UTC().dst(datetime(2009, 1, 1))) + + def test_tzname(self): + self.assertEquals('UTC', UTC().tzname(None)) + self.assertEquals('UTC', UTC().tzname(datetime(2009, 1, 1))) + ############################################################################# # Base clase for testing a particular aspect of timedelta, time, date and # datetime comparisons. class HarmlessMixedComparison: + # Test that __eq__ and __ne__ don't complain for mixed-type comparisons. # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a @@ -3303,7 +3324,6 @@ start += HOUR fstart += HOUR - ############################################################################# # oddballs Index: Modules/datetimemodule.c =================================================================== --- Modules/datetimemodule.c (revision 69265) +++ Modules/datetimemodule.c (working copy) @@ -1,4 +1,4 @@ -/* C implementation for the date/time type documented at + /* C implementation for the date/time type documented at * http://www.zope.org/Members/fdrake/DateTimeWiki/FrontPage */ @@ -103,6 +103,7 @@ static PyTypeObject PyDateTime_DeltaType; static PyTypeObject PyDateTime_TimeType; static PyTypeObject PyDateTime_TZInfoType; +static PyTypeObject PyDateTime_UTCType; /* --------------------------------------------------------------------------- * Math utilities. @@ -751,6 +752,15 @@ #define new_delta(d, s, us, normalize) \ new_delta_ex(d, s, us, normalize, &PyDateTime_DeltaType) +static PyObject * +new_utc_ex(PyTypeObject *type) +{ + return type->tp_alloc(type, 0); +} + +#define new_utc() \ + new_utc_ex(&PyDateTime_UTCType) + /* --------------------------------------------------------------------------- * tzinfo helpers. */ @@ -3046,6 +3056,89 @@ 0, /* tp_free */ }; +static int +utc_init(PyObject *self) +{ + return 0; +} + +static PyObject * +utc_tzname(PyObject *self, PyObject *dt) +{ + return PyString_FromString("UTC"); +} + +static PyObject * +utc_utcoffset(PyObject *self, PyObject *dt) +{ + return new_delta(0, 0, 0, 0); +} + +static PyObject * +utc_dst(PyObject *self, PyObject *dt) +{ + return new_delta(0, 0, 0, 0); +} + +static PyMethodDef utc_methods[] = { + + {"tzname", (PyCFunction)utc_tzname, METH_O, + PyDoc_STR("Always 'UTC'.")}, + + {"utcoffset", (PyCFunction)utc_utcoffset, METH_O, + PyDoc_STR("Always 0 minutes.")}, + + {"dst", (PyCFunction)utc_dst, METH_O, + PyDoc_STR("Always 0 minutes.")}, + + {NULL, NULL} +}; + +static char utc_doc[] = +PyDoc_STR("UTC timezone class."); + +statichere PyTypeObject PyDateTime_UTCType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "datetime.UTC", /* tp_name */ + sizeof(PyDateTime_UTC), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + utc_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + utc_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + &PyDateTime_TZInfoType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ +}; + /* * PyDateTime_Time implementation. */ @@ -3842,9 +3935,19 @@ * precision of a timestamp. */ static PyObject * -datetime_utcnow(PyObject *cls, PyObject *dummy) +datetime_utcnow(PyObject *cls, PyObject *args, PyObject *kw) { - return datetime_best_possible(cls, gmtime, Py_None); + PyObject *utc = Py_None; + PyObject *tz_aware = Py_False; + static char *keywords[] = {"tz_aware", NULL}; + + if (! PyArg_ParseTupleAndKeywords(args, kw, "|b", + keywords, &tz_aware)) + return NULL; + if (tz_aware == Py_True) + utc = new_utc(); + + return datetime_best_possible(cls, gmtime, utc); } /* Return new local datetime from timestamp (Python timestamp -- a double). */ @@ -4567,8 +4670,9 @@ PyDoc_STR("[tz] -> new datetime with tz's local day and time.")}, {"utcnow", (PyCFunction)datetime_utcnow, - METH_NOARGS | METH_CLASS, - PyDoc_STR("Return a new datetime representing UTC day and time.")}, + METH_VARARGS | METH_KEYWORDS | METH_CLASS, + PyDoc_STR("[tz_aware] -> Return a new datetime "\ + "representing UTC day and time.")}, {"fromtimestamp", (PyCFunction)datetime_fromtimestamp, METH_VARARGS | METH_KEYWORDS | METH_CLASS, @@ -4716,6 +4820,7 @@ &PyDateTime_TimeType, &PyDateTime_DeltaType, &PyDateTime_TZInfoType, + &PyDateTime_UTCType, new_date_ex, new_datetime_ex, new_time_ex, @@ -4747,6 +4852,8 @@ return; if (PyType_Ready(&PyDateTime_TZInfoType) < 0) return; + if (PyType_Ready(&PyDateTime_UTCType) < 0) + return; /* timedelta values */ d = PyDateTime_DeltaType.tp_dict; @@ -4840,6 +4947,9 @@ Py_INCREF(&PyDateTime_TZInfoType); PyModule_AddObject(m, "tzinfo", (PyObject *) &PyDateTime_TZInfoType); + Py_INCREF(&PyDateTime_UTCType); + PyModule_AddObject(m, "UTC", (PyObject *) &PyDateTime_UTCType); + x = PyCObject_FromVoidPtrAndDesc(&CAPI, (void*) DATETIME_API_MAGIC, NULL); if (x == NULL)