summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2015-04-01 15:47:07 (GMT)
committerVictor Stinner <victor.stinner@gmail.com>2015-04-01 15:47:07 (GMT)
commit62d1c70eff9498446d2ce3c564fefee7a7e54770 (patch)
treea0a7b3f54ed7baa339dfaaeefc048101d727c63c
parent067bbba7a49aa8bf455465eb5ee75d561cfb1556 (diff)
downloadcpython-62d1c70eff9498446d2ce3c564fefee7a7e54770.zip
cpython-62d1c70eff9498446d2ce3c564fefee7a7e54770.tar.gz
cpython-62d1c70eff9498446d2ce3c564fefee7a7e54770.tar.bz2
Issue #22117, issue #23485: Fix _PyTime_AsMilliseconds() and
_PyTime_AsMicroseconds() rounding. Add also unit tests.
-rw-r--r--Lib/test/test_time.py76
-rw-r--r--Modules/_testcapimodule.c38
-rw-r--r--Python/pytime.c26
3 files changed, 123 insertions, 17 deletions
diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py
index 4747cc6..a9f6fd8 100644
--- a/Lib/test/test_time.py
+++ b/Lib/test/test_time.py
@@ -21,6 +21,8 @@ SIZEOF_INT = sysconfig.get_config_var('SIZEOF_INT') or 4
TIME_MAXYEAR = (1 << 8 * SIZEOF_INT - 1) - 1
TIME_MINYEAR = -TIME_MAXYEAR - 1
+US_TO_NS = 10 ** 3
+MS_TO_NS = 10 ** 6
SEC_TO_NS = 10 ** 9
class _PyTime(enum.IntEnum):
@@ -867,10 +869,6 @@ class TestPyTime_t(unittest.TestCase):
# seconds
(2 * SEC_TO_NS, (2, 0)),
(-3 * SEC_TO_NS, (-3, 0)),
-
- # seconds + nanoseconds
- (1234567000, (1, 234567)),
- (-1234567000, (-2, 765433)),
):
with self.subTest(nanoseconds=ns, timeval=tv, round=rnd):
self.assertEqual(PyTime_AsTimeval(ns, rnd), tv)
@@ -914,6 +912,76 @@ class TestPyTime_t(unittest.TestCase):
with self.subTest(nanoseconds=ns, timespec=ts):
self.assertEqual(PyTime_AsTimespec(ns), ts)
+ def test_milliseconds(self):
+ from _testcapi import PyTime_AsMilliseconds
+ for rnd in ALL_ROUNDING_METHODS:
+ for ns, tv in (
+ # milliseconds
+ (1 * MS_TO_NS, 1),
+ (-2 * MS_TO_NS, -2),
+
+ # seconds
+ (2 * SEC_TO_NS, 2000),
+ (-3 * SEC_TO_NS, -3000),
+ ):
+ with self.subTest(nanoseconds=ns, timeval=tv, round=rnd):
+ self.assertEqual(PyTime_AsMilliseconds(ns, rnd), tv)
+
+ FLOOR = _PyTime.ROUND_FLOOR
+ CEILING = _PyTime.ROUND_CEILING
+ for ns, ms, rnd in (
+ # nanoseconds
+ (1, 0, FLOOR),
+ (1, 1, CEILING),
+ (-1, 0, FLOOR),
+ (-1, -1, CEILING),
+
+ # seconds + nanoseconds
+ (1234 * MS_TO_NS + 1, 1234, FLOOR),
+ (1234 * MS_TO_NS + 1, 1235, CEILING),
+ (-1234 * MS_TO_NS - 1, -1234, FLOOR),
+ (-1234 * MS_TO_NS - 1, -1235, CEILING),
+ ):
+ with self.subTest(nanoseconds=ns, milliseconds=ms, round=rnd):
+ self.assertEqual(PyTime_AsMilliseconds(ns, rnd), ms)
+
+ def test_microseconds(self):
+ from _testcapi import PyTime_AsMicroseconds
+ for rnd in ALL_ROUNDING_METHODS:
+ for ns, tv in (
+ # microseconds
+ (1 * US_TO_NS, 1),
+ (-2 * US_TO_NS, -2),
+
+ # milliseconds
+ (1 * MS_TO_NS, 1000),
+ (-2 * MS_TO_NS, -2000),
+
+ # seconds
+ (2 * SEC_TO_NS, 2000000),
+ (-3 * SEC_TO_NS, -3000000),
+ ):
+ with self.subTest(nanoseconds=ns, timeval=tv, round=rnd):
+ self.assertEqual(PyTime_AsMicroseconds(ns, rnd), tv)
+
+ FLOOR = _PyTime.ROUND_FLOOR
+ CEILING = _PyTime.ROUND_CEILING
+ for ns, ms, rnd in (
+ # nanoseconds
+ (1, 0, FLOOR),
+ (1, 1, CEILING),
+ (-1, 0, FLOOR),
+ (-1, -1, CEILING),
+
+ # seconds + nanoseconds
+ (1234 * US_TO_NS + 1, 1234, FLOOR),
+ (1234 * US_TO_NS + 1, 1235, CEILING),
+ (-1234 * US_TO_NS - 1, -1234, FLOOR),
+ (-1234 * US_TO_NS - 1, -1235, CEILING),
+ ):
+ with self.subTest(nanoseconds=ns, milliseconds=ms, round=rnd):
+ self.assertEqual(PyTime_AsMicroseconds(ns, rnd), ms)
+
if __name__ == "__main__":
unittest.main()
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 253efb6..7b4f239 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -3452,6 +3452,42 @@ test_PyTime_AsTimespec(PyObject *self, PyObject *args)
}
#endif
+static PyObject *
+test_PyTime_AsMilliseconds(PyObject *self, PyObject *args)
+{
+ PY_LONG_LONG ns;
+ int round;
+ _PyTime_t t, ms;
+
+ if (!PyArg_ParseTuple(args, "Li", &ns, &round))
+ return NULL;
+ if (check_time_rounding(round) < 0)
+ return NULL;
+ t = _PyTime_FromNanoseconds(ns);
+ ms = _PyTime_AsMilliseconds(t, round);
+ /* This conversion rely on the fact that _PyTime_t is a number of
+ nanoseconds */
+ return _PyTime_AsNanosecondsObject(ms);
+}
+
+static PyObject *
+test_PyTime_AsMicroseconds(PyObject *self, PyObject *args)
+{
+ PY_LONG_LONG ns;
+ int round;
+ _PyTime_t t, ms;
+
+ if (!PyArg_ParseTuple(args, "Li", &ns, &round))
+ return NULL;
+ if (check_time_rounding(round) < 0)
+ return NULL;
+ t = _PyTime_FromNanoseconds(ns);
+ ms = _PyTime_AsMicroseconds(t, round);
+ /* This conversion rely on the fact that _PyTime_t is a number of
+ nanoseconds */
+ return _PyTime_AsNanosecondsObject(ms);
+}
+
static PyMethodDef TestMethods[] = {
{"raise_exception", raise_exception, METH_VARARGS},
@@ -3621,6 +3657,8 @@ static PyMethodDef TestMethods[] = {
#ifdef HAVE_CLOCK_GETTIME
{"PyTime_AsTimespec", test_PyTime_AsTimespec, METH_VARARGS},
#endif
+ {"PyTime_AsMilliseconds", test_PyTime_AsMilliseconds, METH_VARARGS},
+ {"PyTime_AsMicroseconds", test_PyTime_AsMicroseconds, METH_VARARGS},
{NULL, NULL} /* sentinel */
};
diff --git a/Python/pytime.c b/Python/pytime.c
index 003003b..491bbea 100644
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -19,6 +19,10 @@
#define MS_TO_NS (MS_TO_US * US_TO_NS)
#define SEC_TO_NS (SEC_TO_MS * MS_TO_NS)
+/* Conversion from nanoseconds */
+#define NS_TO_MS (1000 * 1000)
+#define NS_TO_US (1000)
+
static void
error_time_t_overflow(void)
{
@@ -288,33 +292,29 @@ _PyTime_AsNanosecondsObject(_PyTime_t t)
}
static _PyTime_t
-_PyTime_Multiply(_PyTime_t t, unsigned int multiply, _PyTime_round_t round)
+_PyTime_Divide(_PyTime_t t, _PyTime_t k, _PyTime_round_t round)
{
- _PyTime_t k;
- if (multiply < SEC_TO_NS) {
- k = SEC_TO_NS / multiply;
- if (round == _PyTime_ROUND_CEILING)
+ assert(k > 1);
+ if (round == _PyTime_ROUND_CEILING) {
+ if (t >= 0)
return (t + k - 1) / k;
else
- return t / k;
- }
- else {
- k = multiply / SEC_TO_NS;
- return t * k;
+ return (t - (k - 1)) / k;
}
+ else
+ return t / k;
}
_PyTime_t
_PyTime_AsMilliseconds(_PyTime_t t, _PyTime_round_t round)
{
- return _PyTime_Multiply(t, 1000, round);
+ return _PyTime_Divide(t, NS_TO_MS, round);
}
-/* FIXME: write unit tests */
_PyTime_t
_PyTime_AsMicroseconds(_PyTime_t t, _PyTime_round_t round)
{
- return _PyTime_Multiply(t, 1000 * 1000, round);
+ return _PyTime_Divide(t, NS_TO_US, round);
}
static int