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: 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)