]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Issue #22117, issue #23485: Fix _PyTime_AsMilliseconds() and
authorVictor Stinner <victor.stinner@gmail.com>
Wed, 1 Apr 2015 15:47:07 +0000 (17:47 +0200)
committerVictor Stinner <victor.stinner@gmail.com>
Wed, 1 Apr 2015 15:47:07 +0000 (17:47 +0200)
_PyTime_AsMicroseconds() rounding.

Add also unit tests.

Lib/test/test_time.py
Modules/_testcapimodule.c
Python/pytime.c

index 4747cc6ea0dce278d30945eef039488345fa1602..a9f6fd87edbe05271dab3dbd2fb8aea98325561d 100644 (file)
@@ -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()
index 253efb643666184c2d832e2f8971cabf809aaddc..7b4f239637815fe040129f202face5fdf9134a2b 100644 (file)
@@ -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 */
 };
 
index 003003bdcab41b084a45591d0588f9cc0dacd779..491bbea61129c15c2d6ebe939ccc0726df253e3a 100644 (file)
 #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