diff -r 3b7468585156 Doc/library/time.rst --- a/Doc/library/time.rst Thu Feb 16 22:50:59 2012 +0100 +++ b/Doc/library/time.rst Thu Feb 16 23:57:25 2012 +0100 @@ -101,6 +101,7 @@ An explanation of some terminology and c * :class:`int` * :class:`float` + * :class:`datetime.datetime` * :class:`decimal.Decimal` diff -r 3b7468585156 Include/pytime.h --- a/Include/pytime.h Thu Feb 16 22:50:59 2012 +0100 +++ b/Include/pytime.h Thu Feb 16 23:57:25 2012 +0100 @@ -63,6 +63,14 @@ typedef struct /* denominator cannot be zero */ _PyTime_fraction_t denominator; /* the timestamp resolution is 1/divisor */ + enum { + _PYTIME_UNKNOWN_START, + _PYTIME_EPOCH /* 1970.1.1 at 00:00 */ + } start; + enum { + _PYTIME_UNKNOWN_TIMEZONE, + _PYTIME_UTC + } timezone; } _PyTime_t; /* Set a timestamp from a fraction: numerator / denominator. diff -r 3b7468585156 Lib/test/test_os.py --- a/Lib/test/test_os.py Thu Feb 16 22:50:59 2012 +0100 +++ b/Lib/test/test_os.py Thu Feb 16 23:57:25 2012 +0100 @@ -2,6 +2,7 @@ # does add tests for a few functions which have been determined to be more # portable than they had been thought to be. +import datetime import decimal import os import errno @@ -250,10 +251,9 @@ class StatAttributeTests(unittest.TestCa old_value = os.stat_float_times() try: # test invalid timestamp types - self.assertRaises(ValueError, os.stat, self.fname, - timestamp="abc") - self.assertRaises(ValueError, os.stat, self.fname, - timestamp=decimal.Context) + for invalid in ("abc", decimal.Context, datetime.timedelta): + self.assertRaises(ValueError, os.stat, self.fname, + timestamp=invalid) for float_times in (False, True): os.stat_float_times(float_times) @@ -263,7 +263,7 @@ class StatAttributeTests(unittest.TestCa else: self.assertIsInstance(t, int) - for type in (int, float, decimal.Decimal): + for type in (int, float, decimal.Decimal, datetime.datetime): t = os.stat(self.fname, timestamp=type).st_mtime self.assertIsInstance(t, type) finally: diff -r 3b7468585156 Lib/test/test_resource.py --- a/Lib/test/test_resource.py Thu Feb 16 22:50:59 2012 +0100 +++ b/Lib/test/test_resource.py Thu Feb 16 23:57:25 2012 +0100 @@ -1,6 +1,7 @@ from test import support resource = support.import_module('resource') +import datetime import decimal import unittest import time @@ -112,6 +113,8 @@ class ResourceTest(unittest.TestCase): timestamp=type) self.assertIsInstance(usageself.ru_utime, type) self.assertIsInstance(usageself.ru_stime, type) + self.assertRaises(ValueError, resource.getrusage, + resource.RUSAGE_SELF, timestamp=datetime.datetime) def test_main(verbose=None): diff -r 3b7468585156 Lib/test/test_signal.py --- a/Lib/test/test_signal.py Thu Feb 16 22:50:59 2012 +0100 +++ b/Lib/test/test_signal.py Thu Feb 16 23:57:25 2012 +0100 @@ -1,6 +1,7 @@ import unittest from test import support from contextlib import closing +import datetime import decimal import gc import pickle @@ -484,6 +485,8 @@ class ItimerTest(unittest.TestCase): ts = signal.getitimer(self.itimer, timestamp=type) self.assertIsInstance(ts[0], type) self.assertIsInstance(ts[1], type) + self.assertRaises(ValueError, signal.getitimer, + self.itimer, timestamp=datetime.datetime) signal.signal(signal.SIGVTALRM, self.sig_vtalrm) signal.setitimer(self.itimer, 0.3, 0.2) diff -r 3b7468585156 Lib/test/test_time.py --- a/Lib/test/test_time.py Thu Feb 16 22:50:59 2012 +0100 +++ b/Lib/test/test_time.py Thu Feb 16 23:57:25 2012 +0100 @@ -1,3 +1,4 @@ +import datetime import decimal import locale import platform @@ -368,12 +369,35 @@ class TimeTestCase(unittest.TestCase): func, *args = call # test invalid timestamp - for invalid in ("int", decimal.Context): + for invalid in ("int", decimal.Context, datetime.timedelta): self.assertRaises(ValueError, func, *args, timestamp=invalid) for type in (int, float, decimal.Decimal): self.assertIsInstance(func(*args, timestamp=type), type) + def test_timestamp_datetime(self): + # UTC timezone + calls = [ + (time.time,), + (time.mktime, time.localtime()), + ] + if hasattr(time, 'CLOCK_REALTIME') and hasattr(time, 'clock_gettime'): + calls.append((time.clock_gettime, time.CLOCK_REALTIME)) + for func, *args in calls: + t = func(*args, timestamp=datetime.datetime) + self.assertIsInstance(t, datetime.datetime) + self.assertIs(t.tzinfo, datetime.timezone.utc) + + # unspecified starting point + calls = [(time.clock,)] + if hasattr(time, 'clock_gettime') and hasattr(time, 'CLOCK_MONOTONIC'): + calls.append((time.clock_gettime, time.CLOCK_MONOTONIC)) + if hasattr(time, 'clock_getres') and hasattr(time, 'CLOCK_REALTIME'): + calls.append((time.clock_getres, time.CLOCK_REALTIME)) + for func, *args in calls: + self.assertRaises(ValueError, + func, *args, timestamp=datetime.datetime) + def test_wallclock(self): t1 = time.wallclock() t2 = time.wallclock() diff -r 3b7468585156 Modules/posixmodule.c --- a/Modules/posixmodule.c Thu Feb 16 22:50:59 2012 +0100 +++ b/Modules/posixmodule.c Thu Feb 16 23:57:25 2012 +0100 @@ -1052,6 +1052,8 @@ _PyTime_FromFILETIME(_PyTime_t *ts, FILE /* Seconds between 1.1.1601 and 1.1.1970 x 100 ns */ large.QuadPart -= 11644473600 * 10000000; _PyTime_FromFraction(ts, large.QuadPart, 10000000); + ts->start = _PYTIME_UTC; + ts->timezone = _PYTIME_EPOCH; } int @@ -1824,6 +1826,12 @@ _pystat_fromstructstat(STRUCT_STAT *st, _PyTime_FromTime_t(&atime, st->st_atime); _PyTime_FromTime_t(&mtime, st->st_mtime); _PyTime_FromTime_t(&ctime, st->st_ctime); + atime.start = _PYTIME_UTC; + atime.timezone = _PYTIME_EPOCH; + mtime.start = _PYTIME_UTC; + mtime.timezone = _PYTIME_EPOCH; + ctime.start = _PYTIME_UTC; + ctime.timezone = _PYTIME_EPOCH; #if defined(HAVE_STAT_TV_NSEC) atime.numerator = st->st_atim.tv_nsec; atime.denominator = 1000000000; @@ -1870,13 +1878,10 @@ _pystat_fromstructstat(STRUCT_STAT *st, #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME { PyObject *val; - ts.seconds = (long)st->st_birthtime; + _PyTime_FromTime_t(st->st_birthtime); #ifdef HAVE_STAT_TV_NSEC2 ts.numerator = st->st_birthtimespec.tv_nsec; ts.denominator = 1000000000; -#else - ts.numerator = 0; - ts.denominator = 1; #endif if (timestamp == NULL) { if (_stat_float_times) diff -r 3b7468585156 Modules/timemodule.c --- a/Modules/timemodule.c Thu Feb 16 22:50:59 2012 +0100 +++ b/Modules/timemodule.c Thu Feb 16 23:57:25 2012 +0100 @@ -185,6 +185,13 @@ time_clock_gettime(PyObject *self, PyObj return NULL; } _PyTime_FromTimespec(&ts, &tp); +#ifdef CLOCK_REALTIME + if (clk_id == CLOCK_REALTIME) + { + ts.start = _PYTIME_UTC; + ts.timezone = _PYTIME_EPOCH; + } +#endif return _PyTime_AsType(&ts, timestamp); } @@ -767,6 +774,8 @@ time_mktime(PyObject *self, PyObject *ar return NULL; } _PyTime_FromTime_t(&ts, tt); + ts.start = _PYTIME_EPOCH; + ts.timezone = _PYTIME_UTC; return _PyTime_AsType(&ts, timestamp); } @@ -842,6 +851,13 @@ pywallclock(_PyTime_t *ts) if (ret == 0) { _PyTime_FromTimespec(ts, &tp); +#ifdef CLOCK_REALTIME + if (clk_id == CLOCK_REALTIME) + { + ts->start = _PYTIME_UTC; + ts->timezone = _PYTIME_EPOCH; + } +#endif return 0; } diff -r 3b7468585156 Python/pytime.c --- a/Python/pytime.c Thu Feb 16 22:50:59 2012 +0100 +++ b/Python/pytime.c Thu Feb 16 23:57:25 2012 +0100 @@ -78,6 +78,8 @@ _PyTime_FromFraction(_PyTime_t *ts, ts->seconds = 0; ts->numerator = numerator; ts->denominator = denominator; + ts->start = _PYTIME_UNKNOWN_START; + ts->timezone = _PYTIME_UNKNOWN_TIMEZONE; } void @@ -86,6 +88,8 @@ _PyTime_FromTime_t(_PyTime_t *ts, time_t ts->seconds = seconds; ts->numerator = 0; ts->denominator = 1; + ts->start = _PYTIME_UNKNOWN_START; + ts->timezone = _PYTIME_UNKNOWN_TIMEZONE; } int @@ -248,6 +252,8 @@ _PyTime_FromTimeval(_PyTime_t *ts, struc ts->seconds = (time_t)tv->tv_sec; ts->numerator = tv->tv_usec; ts->denominator = MICROSECONDS; + ts->start = _PYTIME_UNKNOWN_START; + ts->timezone = _PYTIME_UNKNOWN_TIMEZONE; } int @@ -280,6 +286,8 @@ _PyTime_FromTimespec(_PyTime_t *ts, stru ts->seconds = tv->tv_sec; ts->numerator = tv->tv_nsec; ts->denominator = NANOSECONDS; + ts->start = _PYTIME_UNKNOWN_START; + ts->timezone = _PYTIME_UNKNOWN_TIMEZONE; } int @@ -313,6 +321,8 @@ _PyTime_Get(_PyTime_t *ts) days). */ value = large.QuadPart - 116444736000000000; _PyTime_FromFraction(ts, value, 10000000); + ts->start = _PYTIME_EPOCH; + ts->timezone = _PYTIME_UTC; #else #ifdef HAVE_GETTIMEOFDAY @@ -340,6 +350,8 @@ _PyTime_Get(_PyTime_t *ts) #endif /* !GETTIMEOFDAY_NO_TZ */ if (err == 0) { _PyTime_FromTimeval(ts, &tv); + ts->start = _PYTIME_EPOCH; + ts->timezone = _PYTIME_UTC; return; } #endif /* !HAVE_GETTIMEOFDAY */ @@ -352,6 +364,8 @@ _PyTime_Get(_PyTime_t *ts) #else /* !HAVE_FTIME */ _PyTime_FromTime_t(ts, time(NULL)); #endif /* !HAVE_FTIME */ + ts->start = _PYTIME_EPOCH; + ts->timezone = _PYTIME_UTC; #endif /* MS_WINDOWS */ } @@ -601,6 +615,86 @@ is_decimal_type(PyObject *obj) return 0; } +int +is_datetime_type(PyObject *obj) +{ + PyObject *module, *name; + _Py_IDENTIFIER(__name__); + _Py_IDENTIFIER(__module__); + + if (!PyType_Check(obj)) + return 0; + + module = _PyObject_GetAttrId(obj, &PyId___module__); + name = _PyObject_GetAttrId(obj, &PyId___name__); + if (module != NULL && PyUnicode_Check(module) + && name != NULL && PyUnicode_Check(name)) { + if (PyUnicode_CompareWithASCIIString(module, "datetime") == 0 + && PyUnicode_CompareWithASCIIString(name, "datetime") == 0) { + Py_DECREF(module); + Py_DECREF(name); + return 1; + } + } + else + PyErr_Clear(); + Py_DECREF(module); + Py_DECREF(name); + return 0; +} + +/* Convert a timestamp to a datetime.datetime object */ +static PyObject* +_PyTime_AsDatetime(const _PyTime_t *ts) +{ + static PyObject *module = NULL; + static PyObject *datetime = NULL; + static PyObject *utc = NULL; + PyObject *decimal, *result; + _Py_IDENTIFIER(fromtimestamp); + + if (ts->start != _PYTIME_EPOCH) { + PyErr_SetString(PyExc_ValueError, "clock has an unspecified starting point"); + return NULL; + } + + if (!module) { + module = PyImport_ImportModuleNoBlock("datetime"); + if (module == NULL) + return NULL; + } + + if (!datetime) { + datetime = PyObject_GetAttrString(module, "datetime"); + if (datetime == NULL) + return NULL; + } + + if (ts->timezone == _PYTIME_UTC && !utc) { + PyObject *timezone; + timezone = PyObject_GetAttrString(module, "timezone"); + if (timezone == NULL) + return NULL; + utc = PyObject_GetAttrString(timezone, "utc"); + Py_DECREF(timezone); + if (utc == NULL) + return NULL; + } + + decimal = _PyTime_AsDecimal(ts); + if (decimal == NULL) + return NULL; + + if (ts->timezone == _PYTIME_UTC) + result = _PyObject_CallMethodId(datetime, &PyId_fromtimestamp, + "OO", decimal, utc); + else + result = _PyObject_CallMethodId(datetime, &PyId_fromtimestamp, + "O", decimal); + Py_DECREF(decimal); + return result; +} + PyObject* _PyTime_AsType(const _PyTime_t *ts, PyObject *format) { @@ -608,12 +702,12 @@ _PyTime_AsType(const _PyTime_t *ts, PyOb if (format == NULL || (PyTypeObject *)format == &PyFloat_Type) return _PyTime_AsFloat(ts); - if ((PyTypeObject *)format == &PyLong_Type) return _PyTime_AsLong(ts); - if (is_decimal_type(format)) return _PyTime_AsDecimal(ts); + if (is_datetime_type(format)) + return _PyTime_AsDatetime(ts); PyErr_Format(PyExc_ValueError, "Unknown timestamp format: %R", format); return NULL;