diff -r 041f0f4b536e Include/pytime.h --- a/Include/pytime.h Sat Feb 04 00:41:12 2012 +0100 +++ b/Include/pytime.h Sat Feb 04 01:18:36 2012 +0100 @@ -52,8 +52,19 @@ 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; +/* Initialize a timestamp: set default values. */ +PyAPI_FUNC(void) _PyTime_reset(_PyTime_t *tp); + /* Similar to POSIX gettimeofday. If system gettimeofday fails or is not available, fall back to lower resolution clocks. */ PyAPI_FUNC(void) _PyTime_get(_PyTime_t *tp); diff -r 041f0f4b536e Lib/test/test_os.py --- a/Lib/test/test_os.py Sat Feb 04 00:41:12 2012 +0100 +++ b/Lib/test/test_os.py Sat Feb 04 01:18:36 2012 +0100 @@ -231,11 +231,12 @@ class StatAttributeTests(unittest.TestCa self.check_stat_attributes(fname) def test_stat_timestamp(self): + import datetime + # test deprecation with warnings.catch_warnings(): warnings.simplefilter("error", DeprecationWarning) self.assertRaises(DeprecationWarning, os.stat_float_times, False) - print("la") with warnings.catch_warnings(): warnings.simplefilter("ignore", DeprecationWarning) @@ -247,6 +248,7 @@ class StatAttributeTests(unittest.TestCa self.assertRaises(ValueError, os.stat, self.fname, timestamp=decimal.Context) + # test supported formats for float_times in (False, True): os.stat_float_times(float_times) t = os.stat(self.fname).st_mtime @@ -258,6 +260,11 @@ class StatAttributeTests(unittest.TestCa for type in (int, float, decimal.Decimal): t = os.stat(self.fname, timestamp=type).st_mtime self.assertIsInstance(t, type) + + # datetime + t = os.stat(self.fname, timestamp=datetime.datetime).st_mtime + self.assertIsInstance(t, datetime.datetime) + self.assertIs(t.tzinfo, datetime.timezone.utc) finally: os.stat_float_times(old_value) diff -r 041f0f4b536e Lib/test/test_time.py --- a/Lib/test/test_time.py Sat Feb 04 00:41:12 2012 +0100 +++ b/Lib/test/test_time.py Sat Feb 04 01:18:36 2012 +0100 @@ -354,6 +354,30 @@ class TimeTestCase(unittest.TestCase): for type in (int, float, decimal.Decimal): self.assertIsInstance(func(*args, timestamp=type), type) + def test_timestamp_datetime(self): + import datetime + # 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 041f0f4b536e Modules/posixmodule.c --- a/Modules/posixmodule.c Sat Feb 04 00:41:12 2012 +0100 +++ b/Modules/posixmodule.c Sat Feb 04 01:18:36 2012 +0100 @@ -1730,9 +1730,12 @@ fill_time(PyObject *v, int index, time_t if (timestamp == NULL && _stat_float_times) timestamp = (PyObject*)&PyFloat_Type; if (timestamp != NULL) { + _PyTime_reset(&ts); ts.seconds = sec; ts.numerator = nsec; ts.denominator = 1000000000; + ts.start = _PYTIME_UTC; + ts.timezone = _PYTIME_EPOCH; fval = _PyTime_Convert(&ts, timestamp); } else { diff -r 041f0f4b536e Modules/timemodule.c --- a/Modules/timemodule.c Sat Feb 04 00:41:12 2012 +0100 +++ b/Modules/timemodule.c Sat Feb 04 01:18:36 2012 +0100 @@ -89,7 +89,7 @@ pyclock(_PyTime_t *ts) "or its value cannot be represented"); return -1; } - ts->seconds = 0; + _PyTime_reset(ts); assert(sizeof(clock_t) <= sizeof(_PyTime_fraction_t)); ts->numerator = Py_SAFE_DOWNCAST(processor_time, clock_t, _PyTime_fraction_t); @@ -123,7 +123,7 @@ win32_pyclock(_PyTime_t *ts) QueryPerformanceCounter(&now); dt = now.QuadPart - ctrStart.QuadPart; - ts->seconds = 0; + _PyTime_reset(ts); assert(sizeof(LONGLONG) <= sizeof(_PyTime_fraction_t)); ts->numerator = Py_SAFE_DOWNCAST(dt, LONGLONG, _PyTime_fraction_t); @@ -186,9 +186,17 @@ time_clock_gettime(PyObject *self, PyObj PyErr_SetFromErrno(PyExc_IOError); return NULL; } + _PyTime_reset(&ts); ts.seconds = tp.tv_sec; ts.numerator = tp.tv_nsec; ts.denominator = 1000000000; +#ifdef CLOCK_REALTIME + if (clk_id == CLOCK_REALTIME) + { + ts.start = _PYTIME_UTC; + ts.timezone = _PYTIME_EPOCH; + } +#endif return _PyTime_Convert(&ts, timestamp); } @@ -218,6 +226,7 @@ time_clock_getres(PyObject *self, PyObje PyErr_SetFromErrno(PyExc_IOError); return NULL; } + _PyTime_reset(&ts); ts.seconds = tp.tv_sec; ts.numerator = tp.tv_nsec; ts.denominator = 1000000000; @@ -772,9 +781,10 @@ time_mktime(PyObject *self, PyObject *ar "mktime argument out of range"); return NULL; } + _PyTime_reset(&ts); ts.seconds = tt; - ts.numerator = 0; - ts.denominator = 1; + ts.start = _PYTIME_EPOCH; + ts.timezone = _PYTIME_UTC; return _PyTime_Convert(&ts, timestamp); } @@ -849,9 +859,17 @@ pywallclock(_PyTime_t *ts) ret = clock_gettime(clk_id, &tp); if (ret == 0) { + _PyTime_reset(ts); ts->seconds = tp.tv_sec; ts->numerator = tp.tv_nsec; ts->denominator = 1000000000; +#ifdef CLOCK_REALTIME + if (clk_id == CLOCK_REALTIME) + { + ts->start = _PYTIME_UTC; + ts->timezone = _PYTIME_EPOCH; + } +#endif return 0; } diff -r 041f0f4b536e Python/pytime.c --- a/Python/pytime.c Sat Feb 04 00:41:12 2012 +0100 +++ b/Python/pytime.c Sat Feb 04 01:18:36 2012 +0100 @@ -22,6 +22,16 @@ extern int ftime(struct timeb *); #define MICROSECONDS 1000000 void +_PyTime_reset(_PyTime_t *tp) +{ + tp->seconds = 0; + tp->numerator = 0; + tp->denominator = 1; + tp->start = _PYTIME_UNKNOWN_START; + tp->timezone = _PYTIME_UNKNOWN_TIMEZONE; +} + +void _PyTime_get(_PyTime_t *ts) { /* There are three ways to get the time: @@ -41,6 +51,9 @@ _PyTime_get(_PyTime_t *ts) struct timeb t; #endif + _PyTime_reset(ts); + ts->start = _PYTIME_EPOCH; + ts->timezone = _PYTIME_UTC; #ifdef HAVE_GETTIMEOFDAY #ifdef GETTIMEOFDAY_NO_TZ err = gettimeofday(&tv); @@ -63,8 +76,6 @@ _PyTime_get(_PyTime_t *ts) ts->denominator = 1000; #else /* !HAVE_FTIME */ ts->seconds = time(NULL); - ts->numerator = 0; - ts->denominator = 1; #endif /* !HAVE_FTIME */ } @@ -326,6 +337,58 @@ error: return NULL; } +/* Convert a timestamp to a datetime.datetime object */ +static PyObject* +_PyTime_AsDatetime(_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_Convert(_PyTime_t *ts, PyObject *format) { @@ -350,6 +413,9 @@ _PyTime_Convert(_PyTime_t *ts, PyObject if (PyUnicode_CompareWithASCIIString(module, "decimal") == 0 && PyUnicode_CompareWithASCIIString(name, "Decimal") == 0) return _PyTime_AsDecimal(ts); + if (PyUnicode_CompareWithASCIIString(module, "datetime") == 0 + && PyUnicode_CompareWithASCIIString(name, "datetime") == 0) + return _PyTime_AsDatetime(ts); } else PyErr_Clear();