mirror of
https://github.com/python/cpython.git
synced 2025-09-26 10:19:53 +00:00
Issue #22117: time.monotonic() now uses the new _PyTime_t API
* Add _PyTime_FromNanoseconds() * Add _PyTime_AsSecondsDouble() * Add unit tests for _PyTime_AsSecondsDouble()
This commit is contained in:
parent
52d1493c0c
commit
4bfb460d88
5 changed files with 121 additions and 15 deletions
|
@ -119,12 +119,18 @@ typedef PY_INT64_T _PyTime_t;
|
||||||
# error "_PyTime_t need signed 64-bit integer type"
|
# error "_PyTime_t need signed 64-bit integer type"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Create a timestamp from a number of nanoseconds (C long). */
|
||||||
|
PyAPI_FUNC(_PyTime_t) _PyTime_FromNanoseconds(PY_LONG_LONG ns);
|
||||||
|
|
||||||
/* Convert a Python float or int to a timetamp.
|
/* Convert a Python float or int to a timetamp.
|
||||||
Raise an exception and return -1 on error, return 0 on success. */
|
Raise an exception and return -1 on error, return 0 on success. */
|
||||||
PyAPI_FUNC(int) _PyTime_FromSecondsObject(_PyTime_t *t,
|
PyAPI_FUNC(int) _PyTime_FromSecondsObject(_PyTime_t *t,
|
||||||
PyObject *obj,
|
PyObject *obj,
|
||||||
_PyTime_round_t round);
|
_PyTime_round_t round);
|
||||||
|
|
||||||
|
/* Convert a timestamp to a number of seconds as a C double. */
|
||||||
|
PyAPI_FUNC(double) _PyTime_AsSecondsDouble(_PyTime_t t);
|
||||||
|
|
||||||
/* Convert timestamp to a number of milliseconds (10^-3 seconds). */
|
/* Convert timestamp to a number of milliseconds (10^-3 seconds). */
|
||||||
PyAPI_FUNC(_PyTime_t) _PyTime_AsMilliseconds(_PyTime_t t,
|
PyAPI_FUNC(_PyTime_t) _PyTime_AsMilliseconds(_PyTime_t t,
|
||||||
_PyTime_round_t round);
|
_PyTime_round_t round);
|
||||||
|
@ -133,7 +139,8 @@ PyAPI_FUNC(_PyTime_t) _PyTime_AsMilliseconds(_PyTime_t t,
|
||||||
object. */
|
object. */
|
||||||
PyAPI_FUNC(PyObject *) _PyTime_AsNanosecondsObject(_PyTime_t t);
|
PyAPI_FUNC(PyObject *) _PyTime_AsNanosecondsObject(_PyTime_t t);
|
||||||
|
|
||||||
/* Convert a timestamp to a timeval structure. */
|
/* Convert a timestamp to a timeval structure (microsecond resolution).
|
||||||
|
Raise an exception and return -1 on error, return 0 on success. */
|
||||||
PyAPI_FUNC(int) _PyTime_AsTimeval(_PyTime_t t,
|
PyAPI_FUNC(int) _PyTime_AsTimeval(_PyTime_t t,
|
||||||
struct timeval *tv,
|
struct timeval *tv,
|
||||||
_PyTime_round_t round);
|
_PyTime_round_t round);
|
||||||
|
@ -147,6 +154,18 @@ PyAPI_FUNC(int) _PyTime_AsTimeval(_PyTime_t t,
|
||||||
is available and works. */
|
is available and works. */
|
||||||
PyAPI_FUNC(_PyTime_t) _PyTime_GetMonotonicClock(void);
|
PyAPI_FUNC(_PyTime_t) _PyTime_GetMonotonicClock(void);
|
||||||
|
|
||||||
|
/* Get the time of a monotonic clock, i.e. a clock that cannot go backwards.
|
||||||
|
The clock is not affected by system clock updates. The reference point of
|
||||||
|
the returned value is undefined, so that only the difference between the
|
||||||
|
results of consecutive calls is valid.
|
||||||
|
|
||||||
|
Fill info (if set) with information of the function used to get the time.
|
||||||
|
|
||||||
|
Return 0 on success, raise an exception and return -1 on error. */
|
||||||
|
PyAPI_FUNC(int) _PyTime_GetMonotonicClockWithInfo(
|
||||||
|
_PyTime_t *t,
|
||||||
|
_Py_clock_info_t *info);
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ SIZEOF_INT = sysconfig.get_config_var('SIZEOF_INT') or 4
|
||||||
TIME_MAXYEAR = (1 << 8 * SIZEOF_INT - 1) - 1
|
TIME_MAXYEAR = (1 << 8 * SIZEOF_INT - 1) - 1
|
||||||
TIME_MINYEAR = -TIME_MAXYEAR - 1
|
TIME_MINYEAR = -TIME_MAXYEAR - 1
|
||||||
|
|
||||||
|
SEC_TO_NS = 10 ** 9
|
||||||
|
|
||||||
class _PyTime(enum.IntEnum):
|
class _PyTime(enum.IntEnum):
|
||||||
# Round towards zero
|
# Round towards zero
|
||||||
|
@ -770,9 +771,7 @@ class TestPytime(unittest.TestCase):
|
||||||
@support.cpython_only
|
@support.cpython_only
|
||||||
class TestPyTime_t(unittest.TestCase):
|
class TestPyTime_t(unittest.TestCase):
|
||||||
def test_FromSecondsObject(self):
|
def test_FromSecondsObject(self):
|
||||||
from _testcapi import pytime_fromsecondsobject
|
from _testcapi import PyTime_FromSecondsObject
|
||||||
SEC_TO_NS = 10 ** 9
|
|
||||||
MAX_SEC = 2 ** 63 // 10 ** 9
|
|
||||||
|
|
||||||
# Conversion giving the same result for all rounding methods
|
# Conversion giving the same result for all rounding methods
|
||||||
for rnd in ALL_ROUNDING_METHODS:
|
for rnd in ALL_ROUNDING_METHODS:
|
||||||
|
@ -811,21 +810,21 @@ class TestPyTime_t(unittest.TestCase):
|
||||||
(2**25 , 33554432000000000),
|
(2**25 , 33554432000000000),
|
||||||
(2**25 + 1e-9, 33554432000000000),
|
(2**25 + 1e-9, 33554432000000000),
|
||||||
|
|
||||||
# close to 2^63 nanoseconds
|
# close to 2^63 nanoseconds (_PyTime_t limit)
|
||||||
(9223372036, 9223372036 * SEC_TO_NS),
|
(9223372036, 9223372036 * SEC_TO_NS),
|
||||||
(9223372036.0, 9223372036 * SEC_TO_NS),
|
(9223372036.0, 9223372036 * SEC_TO_NS),
|
||||||
(-9223372036, -9223372036 * SEC_TO_NS),
|
(-9223372036, -9223372036 * SEC_TO_NS),
|
||||||
(-9223372036.0, -9223372036 * SEC_TO_NS),
|
(-9223372036.0, -9223372036 * SEC_TO_NS),
|
||||||
):
|
):
|
||||||
with self.subTest(obj=obj, round=rnd, timestamp=ts):
|
with self.subTest(obj=obj, round=rnd, timestamp=ts):
|
||||||
self.assertEqual(pytime_fromsecondsobject(obj, rnd), ts)
|
self.assertEqual(PyTime_FromSecondsObject(obj, rnd), ts)
|
||||||
|
|
||||||
with self.subTest(round=rnd):
|
with self.subTest(round=rnd):
|
||||||
with self.assertRaises(OverflowError):
|
with self.assertRaises(OverflowError):
|
||||||
pytime_fromsecondsobject(9223372037, rnd)
|
PyTime_FromSecondsObject(9223372037, rnd)
|
||||||
pytime_fromsecondsobject(9223372037.0, rnd)
|
PyTime_FromSecondsObject(9223372037.0, rnd)
|
||||||
pytime_fromsecondsobject(-9223372037, rnd)
|
PyTime_FromSecondsObject(-9223372037, rnd)
|
||||||
pytime_fromsecondsobject(-9223372037.0, rnd)
|
PyTime_FromSecondsObject(-9223372037.0, rnd)
|
||||||
|
|
||||||
# Conversion giving different results depending on the rounding method
|
# Conversion giving different results depending on the rounding method
|
||||||
UP = _PyTime.ROUND_UP
|
UP = _PyTime.ROUND_UP
|
||||||
|
@ -850,7 +849,52 @@ class TestPyTime_t(unittest.TestCase):
|
||||||
(-0.9999999999, -1000000000, UP),
|
(-0.9999999999, -1000000000, UP),
|
||||||
):
|
):
|
||||||
with self.subTest(obj=obj, round=rnd, timestamp=ts):
|
with self.subTest(obj=obj, round=rnd, timestamp=ts):
|
||||||
self.assertEqual(pytime_fromsecondsobject(obj, rnd), ts)
|
self.assertEqual(PyTime_FromSecondsObject(obj, rnd), ts)
|
||||||
|
|
||||||
|
def test_AsSecondsDouble(self):
|
||||||
|
from _testcapi import PyTime_AsSecondsDouble
|
||||||
|
|
||||||
|
for nanoseconds, seconds in (
|
||||||
|
# near 1 nanosecond
|
||||||
|
( 0, 0.0),
|
||||||
|
( 1, 1e-9),
|
||||||
|
(-1, -1e-9),
|
||||||
|
|
||||||
|
# near 1 second
|
||||||
|
(SEC_TO_NS + 1, 1.0 + 1e-9),
|
||||||
|
(SEC_TO_NS, 1.0),
|
||||||
|
(SEC_TO_NS - 1, 1.0 - 1e-9),
|
||||||
|
|
||||||
|
# a few seconds
|
||||||
|
(123 * SEC_TO_NS, 123.0),
|
||||||
|
(-567 * SEC_TO_NS, -567.0),
|
||||||
|
|
||||||
|
# nanosecond are kept for value <= 2^23 seconds
|
||||||
|
(4194303999999999, 2**22 - 1e-9),
|
||||||
|
(4194304000000000, 2**22),
|
||||||
|
(4194304000000001, 2**22 + 1e-9),
|
||||||
|
|
||||||
|
# start loosing precision for value > 2^23 seconds
|
||||||
|
(8388608000000002, 2**23 + 1e-9),
|
||||||
|
|
||||||
|
# nanoseconds are lost for value > 2^23 seconds
|
||||||
|
(16777215999999998, 2**24 - 1e-9),
|
||||||
|
(16777215999999999, 2**24 - 1e-9),
|
||||||
|
(16777216000000000, 2**24 ),
|
||||||
|
(16777216000000001, 2**24 ),
|
||||||
|
(16777216000000002, 2**24 + 2e-9),
|
||||||
|
|
||||||
|
(33554432000000000, 2**25 ),
|
||||||
|
(33554432000000002, 2**25 ),
|
||||||
|
(33554432000000004, 2**25 + 4e-9),
|
||||||
|
|
||||||
|
# close to 2^63 nanoseconds (_PyTime_t limit)
|
||||||
|
(9223372036 * SEC_TO_NS, 9223372036.0),
|
||||||
|
(-9223372036 * SEC_TO_NS, -9223372036.0),
|
||||||
|
):
|
||||||
|
with self.subTest(nanoseconds=nanoseconds, seconds=seconds):
|
||||||
|
self.assertEqual(PyTime_AsSecondsDouble(nanoseconds),
|
||||||
|
seconds)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -3394,6 +3394,20 @@ test_pytime_fromsecondsobject(PyObject *self, PyObject *args)
|
||||||
return _PyTime_AsNanosecondsObject(ts);
|
return _PyTime_AsNanosecondsObject(ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
test_pytime_assecondsdouble(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
PY_LONG_LONG ns;
|
||||||
|
_PyTime_t ts;
|
||||||
|
double d;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "L", &ns))
|
||||||
|
return NULL;
|
||||||
|
ts = _PyTime_FromNanoseconds(ns);
|
||||||
|
d = _PyTime_AsSecondsDouble(ts);
|
||||||
|
return PyFloat_FromDouble(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyMethodDef TestMethods[] = {
|
static PyMethodDef TestMethods[] = {
|
||||||
{"raise_exception", raise_exception, METH_VARARGS},
|
{"raise_exception", raise_exception, METH_VARARGS},
|
||||||
|
@ -3557,7 +3571,8 @@ static PyMethodDef TestMethods[] = {
|
||||||
return_null_without_error, METH_NOARGS},
|
return_null_without_error, METH_NOARGS},
|
||||||
{"return_result_with_error",
|
{"return_result_with_error",
|
||||||
return_result_with_error, METH_NOARGS},
|
return_result_with_error, METH_NOARGS},
|
||||||
{"pytime_fromsecondsobject", test_pytime_fromsecondsobject, METH_VARARGS},
|
{"PyTime_FromSecondsObject", test_pytime_fromsecondsobject, METH_VARARGS},
|
||||||
|
{"PyTime_AsSecondsDouble", test_pytime_assecondsdouble, METH_VARARGS},
|
||||||
{NULL, NULL} /* sentinel */
|
{NULL, NULL} /* sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -887,12 +887,14 @@ should not be relied on.");
|
||||||
static PyObject *
|
static PyObject *
|
||||||
pymonotonic(_Py_clock_info_t *info)
|
pymonotonic(_Py_clock_info_t *info)
|
||||||
{
|
{
|
||||||
_PyTime_timeval tv;
|
_PyTime_t t;
|
||||||
if (_PyTime_monotonic_info(&tv, info) < 0) {
|
double d;
|
||||||
|
if (_PyTime_GetMonotonicClockWithInfo(&t, info) < 0) {
|
||||||
assert(info != NULL);
|
assert(info != NULL);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return PyFloat_FromDouble((double)tv.tv_sec + tv.tv_usec * 1e-6);
|
d = _PyTime_AsSecondsDouble(t);
|
||||||
|
return PyFloat_FromDouble(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
|
|
@ -405,6 +405,15 @@ _PyTime_overflow(void)
|
||||||
"timestamp too large to convert to C _PyTime_t");
|
"timestamp too large to convert to C _PyTime_t");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_PyTime_t
|
||||||
|
_PyTime_FromNanoseconds(PY_LONG_LONG ns)
|
||||||
|
{
|
||||||
|
_PyTime_t t;
|
||||||
|
assert(sizeof(PY_LONG_LONG) <= sizeof(_PyTime_t));
|
||||||
|
t = Py_SAFE_DOWNCAST(ns, PY_LONG_LONG, _PyTime_t);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
#if !defined(MS_WINDOWS) && !defined(__APPLE__)
|
#if !defined(MS_WINDOWS) && !defined(__APPLE__)
|
||||||
static int
|
static int
|
||||||
_PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts)
|
_PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts)
|
||||||
|
@ -470,6 +479,17 @@ _PyTime_FromSecondsObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double
|
||||||
|
_PyTime_AsSecondsDouble(_PyTime_t t)
|
||||||
|
{
|
||||||
|
_PyTime_t sec, ns;
|
||||||
|
/* Divide using integers to avoid rounding issues on the integer part.
|
||||||
|
1e-9 cannot be stored exactly in IEEE 64-bit. */
|
||||||
|
sec = t / SEC_TO_NS;
|
||||||
|
ns = t % SEC_TO_NS;
|
||||||
|
return (double)sec + (double)ns * 1e-9;
|
||||||
|
}
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
_PyTime_AsNanosecondsObject(_PyTime_t t)
|
_PyTime_AsNanosecondsObject(_PyTime_t t)
|
||||||
{
|
{
|
||||||
|
@ -660,6 +680,12 @@ _PyTime_GetMonotonicClock(void)
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
_PyTime_GetMonotonicClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
|
||||||
|
{
|
||||||
|
return pymonotonic_new(tp, info, 1);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
_PyTime_Init(void)
|
_PyTime_Init(void)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue