diff -r 2fcd99929dba Doc/library/datetime.rst --- a/Doc/library/datetime.rst Mon Mar 30 10:00:40 2015 +0300 +++ b/Doc/library/datetime.rst Mon Mar 30 13:00:52 2015 +0300 @@ -421,7 +421,6 @@ Other constructors, all class methods: :c:func:`localtime` function. Raise :exc:`OSError` instead of :exc:`ValueError` on :c:func:`localtime` failure. - .. classmethod:: date.fromordinal(ordinal) Return the date corresponding to the proleptic Gregorian ordinal, where January @@ -752,6 +751,9 @@ Other constructors, all class methods: instead of :exc:`ValueError` on :c:func:`localtime` or :c:func:`gmtime` failure. + .. versionchanged:: 3.5 + Added support of :class:`~decimal.Decimal` timestamps. + .. classmethod:: datetime.utcfromtimestamp(timestamp) @@ -779,6 +781,9 @@ Other constructors, all class methods: :c:func:`gmtime` function. Raise :exc:`OSError` instead of :exc:`ValueError` on :c:func:`gmtime` failure. + .. versionchanged:: 3.5 + Added support of :class:`~decimal.Decimal` timestamps. + .. classmethod:: datetime.fromordinal(ordinal) diff -r 2fcd99929dba Lib/datetime.py --- a/Lib/datetime.py Mon Mar 30 10:00:40 2015 +0300 +++ b/Lib/datetime.py Mon Mar 30 13:00:52 2015 +0300 @@ -1375,8 +1375,11 @@ class datetime(date): converter = _time.localtime if tz is None else _time.gmtime - t, frac = divmod(t, 1.0) - us = int(frac * 1e6) + t, frac = divmod(t, 1) + if frac < 0: + t -= 1 + frac += 1 + us = int(frac * 1000000) # long form required for decimal support # If timestamp is less than one microsecond smaller than a # full second, us can be rounded up to 1000000. In this case, @@ -1395,8 +1398,11 @@ class datetime(date): @classmethod def utcfromtimestamp(cls, t): """Construct a naive UTC datetime from a POSIX timestamp.""" - t, frac = divmod(t, 1.0) - us = int(frac * 1e6) + t, frac = divmod(t, 1) + if frac < 0: + t -= 1 + frac += 1 + us = int(frac * 1000000) # long form required for decimal support # If timestamp is less than one microsecond smaller than a # full second, us can be rounded up to 1000000. In this case, diff -r 2fcd99929dba Lib/test/datetimetester.py --- a/Lib/test/datetimetester.py Mon Mar 30 10:00:40 2015 +0300 +++ b/Lib/test/datetimetester.py Mon Mar 30 13:00:52 2015 +0300 @@ -1799,6 +1799,8 @@ class TestDateTime(TestDate): expected = time.localtime(ts) got = self.theclass.fromtimestamp(ts) self.verify_field_equality(expected, got) + got = self.theclass.fromtimestamp(decimal.Decimal(ts)) + self.verify_field_equality(expected, got) def test_utcfromtimestamp(self): import time @@ -1807,6 +1809,8 @@ class TestDateTime(TestDate): expected = time.gmtime(ts) got = self.theclass.utcfromtimestamp(ts) self.verify_field_equality(expected, got) + got = self.theclass.utcfromtimestamp(decimal.Decimal(ts)) + self.verify_field_equality(expected, got) # Run with US-style DST rules: DST begins 2 a.m. on second Sunday in # March (M3.2.0) and ends 2 a.m. on first Sunday in November (M11.1.0). @@ -1878,6 +1882,39 @@ class TestDateTime(TestDate): self.assertEqual(t.second, 0) self.assertEqual(t.microsecond, 999999) + def test_microsecond_decimal(self): + for fts in [self.theclass.fromtimestamp, + self.theclass.utcfromtimestamp]: + zero = fts(decimal.Decimal(0)) + self.assertEqual(zero.second, 0) + self.assertEqual(zero.microsecond, 0) + try: + minus_one = fts(decimal.Decimal('-0.000001')) + except OSError: + # localtime(-1) and gmtime(-1) is not supported on Windows + pass + else: + self.assertEqual(minus_one.second, 59) + self.assertEqual(minus_one.microsecond, 999999) + + t = fts(decimal.Decimal('-0.00000001')) + self.assertEqual(t, minus_one) + t = fts(decimal.Decimal('-0.0000009')) + self.assertEqual(t, minus_one) + t = fts(decimal.Decimal('-0.0000001')) + self.assertEqual(t, minus_one) + + t = fts(decimal.Decimal('0.0000001')) + self.assertEqual(t, zero) + t = fts(decimal.Decimal('0.0000009')) + self.assertEqual(t, zero) + t = fts(decimal.Decimal('0.99999949')) + self.assertEqual(t.second, 0) + self.assertEqual(t.microsecond, 999999) + t = fts(decimal.Decimal('0.9999999')) + self.assertEqual(t.second, 0) + self.assertEqual(t.microsecond, 999999) + def test_insane_fromtimestamp(self): # It's possible that some platform maps time_t to double, # and that this test will fail there. This test should diff -r 2fcd99929dba Modules/posixmodule.c --- a/Modules/posixmodule.c Mon Mar 30 10:00:40 2015 +0300 +++ b/Modules/posixmodule.c Mon Mar 30 13:00:52 2015 +0300 @@ -6122,7 +6122,7 @@ os_utime_impl(PyModuleDef *module, path_ if (!PyTuple_CheckExact(times) || (PyTuple_Size(times) != 2)) { PyErr_SetString(PyExc_TypeError, "utime: 'times' must be either" - " a tuple of two ints or None"); + " a tuple of two numbers or None"); goto exit; } utime.now = 0; diff -r 2fcd99929dba Python/pytime.c --- a/Python/pytime.c Mon Mar 30 10:00:40 2015 +0300 +++ b/Python/pytime.c Mon Mar 30 13:00:52 2015 +0300 @@ -58,9 +58,8 @@ PyObject * static int _PyTime_ObjectToDenominator(PyObject *obj, time_t *sec, long *numerator, - double denominator, _PyTime_round_t round) + long denominator, _PyTime_round_t round) { - assert(denominator <= LONG_MAX); if (PyFloat_Check(obj)) { double d, intpart, err; /* volatile avoids unsafe optimization on float enabled by gcc -O3 */ @@ -95,13 +94,75 @@ static int *numerator = (long)floatpart; return 0; } - else { + else if (PyIndex_Check(obj)) { *sec = _PyLong_AsTime_t(obj); if (*sec == (time_t)-1 && PyErr_Occurred()) return -1; *numerator = 0; return 0; } + else if (PyNumber_Check(obj)) { + PyObject *intpart, *fracpart, *denomobj, *numerobj; + int cmp; + + intpart = PyNumber_Long(obj); + if (intpart == NULL) + return -1; + *sec = _PyLong_AsTime_t(intpart); + if (*sec == (time_t)-1 && PyErr_Occurred()) { + Py_DECREF(intpart); + return -1; + } + fracpart = PyNumber_Subtract(obj, intpart); + Py_DECREF(intpart); + denomobj = PyLong_FromLong(denominator); + if (intpart == NULL) { + Py_DECREF(denomobj); + return -1; + } + numerobj = PyNumber_Multiply(fracpart, denomobj); + Py_DECREF(fracpart); + Py_DECREF(denomobj); + if (numerobj == NULL) + return -1; + intpart = PyNumber_Long(numerobj); + if (intpart == NULL) { + Py_DECREF(numerobj); + return -1; + } + cmp = PyObject_RichCompareBool(intpart, numerobj, + round == _PyTime_ROUND_CEILING ? Py_LT : Py_GT); + Py_DECREF(numerobj); + if (cmp < 0) { + Py_DECREF(intpart); + return -1; + } + *numerator = PyLong_AsLong(intpart); + Py_DECREF(intpart); + if (*numerator == -1 && PyErr_Occurred()) + return -1; + if (cmp) { + if (round == _PyTime_ROUND_CEILING) + *numerator += 1; + else + *numerator -= 1; + } + if (*numerator < 0) { + *sec -= 1; + *numerator += denominator; + } + else if (*numerator >= denominator) { + *sec += 1; + *numerator -= denominator; + } + return 0; + } + else { + PyErr_Format(PyExc_TypeError, + "a number is required (got type %.200s)", + Py_TYPE(obj)->tp_name); + return -1; + } } int @@ -137,14 +198,14 @@ int _PyTime_ObjectToTimespec(PyObject *obj, time_t *sec, long *nsec, _PyTime_round_t round) { - return _PyTime_ObjectToDenominator(obj, sec, nsec, 1e9, round); + return _PyTime_ObjectToDenominator(obj, sec, nsec, 1000000000, round); } int _PyTime_ObjectToTimeval(PyObject *obj, time_t *sec, long *usec, _PyTime_round_t round) { - return _PyTime_ObjectToDenominator(obj, sec, usec, 1e6, round); + return _PyTime_ObjectToDenominator(obj, sec, usec, 1000000, round); } static void