ROUND_FLOOR = 0
# Round towards infinity (+inf)
ROUND_CEILING = 1
+ # Round to nearest with ties going away from zero
+ ROUND_HALF_UP = 2
-ALL_ROUNDING_METHODS = (_PyTime.ROUND_FLOOR, _PyTime.ROUND_CEILING)
+ALL_ROUNDING_METHODS = (_PyTime.ROUND_FLOOR, _PyTime.ROUND_CEILING,
+ _PyTime.ROUND_HALF_UP)
class TimeTestCase(unittest.TestCase):
(123.0, 123 * SEC_TO_NS),
(-7.0, -7 * SEC_TO_NS),
- # nanosecond are kept for value <= 2^23 seconds
+ # nanosecond are kept for value <= 2^23 seconds,
+ # except 2**23-1e-9 with HALF_UP
(2**22 - 1e-9, 4194303999999999),
(2**22, 4194304000000000),
(2**22 + 1e-9, 4194304000000001),
- (2**23 - 1e-9, 8388607999999999),
(2**23, 8388608000000000),
# start loosing precision for value > 2^23 seconds
# Conversion giving different results depending on the rounding method
FLOOR = _PyTime.ROUND_FLOOR
CEILING = _PyTime.ROUND_CEILING
+ HALF_UP = _PyTime.ROUND_HALF_UP
for obj, ts, rnd in (
# close to zero
( 1e-10, 0, FLOOR),
( 1e-10, 1, CEILING),
+ ( 1e-10, 0, HALF_UP),
(-1e-10, -1, FLOOR),
(-1e-10, 0, CEILING),
+ (-1e-10, 0, HALF_UP),
# test rounding of the last nanosecond
( 1.1234567899, 1123456789, FLOOR),
( 1.1234567899, 1123456790, CEILING),
+ ( 1.1234567899, 1123456790, HALF_UP),
(-1.1234567899, -1123456790, FLOOR),
(-1.1234567899, -1123456789, CEILING),
+ (-1.1234567899, -1123456790, HALF_UP),
# close to 1 second
( 0.9999999999, 999999999, FLOOR),
( 0.9999999999, 1000000000, CEILING),
+ ( 0.9999999999, 1000000000, HALF_UP),
(-0.9999999999, -1000000000, FLOOR),
(-0.9999999999, -999999999, CEILING),
+ (-0.9999999999, -1000000000, HALF_UP),
+
+ # close to 2^23 seconds
+ (2**23 - 1e-9, 8388607999999999, FLOOR),
+ (2**23 - 1e-9, 8388607999999999, CEILING),
+ (2**23 - 1e-9, 8388608000000000, HALF_UP),
):
with self.subTest(obj=obj, round=rnd, timestamp=ts):
self.assertEqual(PyTime_FromSecondsObject(obj, rnd), ts)
FLOOR = _PyTime.ROUND_FLOOR
CEILING = _PyTime.ROUND_CEILING
+ HALF_UP = _PyTime.ROUND_HALF_UP
for ns, tv, rnd in (
# nanoseconds
(1, (0, 0), FLOOR),
(1, (0, 1), CEILING),
+ (1, (0, 0), HALF_UP),
(-1, (-1, 999999), FLOOR),
(-1, (0, 0), CEILING),
+ (-1, (0, 0), HALF_UP),
# seconds + nanoseconds
(1234567001, (1, 234567), FLOOR),
(1234567001, (1, 234568), CEILING),
+ (1234567001, (1, 234567), HALF_UP),
(-1234567001, (-2, 765432), FLOOR),
(-1234567001, (-2, 765433), CEILING),
+ (-1234567001, (-2, 765433), HALF_UP),
+
+ # half up
+ (499, (0, 0), HALF_UP),
+ (500, (0, 1), HALF_UP),
+ (501, (0, 1), HALF_UP),
+ (999, (0, 1), HALF_UP),
+ (-499, (0, 0), HALF_UP),
+ (-500, (0, 0), HALF_UP),
+ (-501, (-1, 999999), HALF_UP),
+ (-999, (-1, 999999), HALF_UP),
):
with self.subTest(nanoseconds=ns, timeval=tv, round=rnd):
self.assertEqual(PyTime_AsTimeval(ns, rnd), tv)
FLOOR = _PyTime.ROUND_FLOOR
CEILING = _PyTime.ROUND_CEILING
+ HALF_UP = _PyTime.ROUND_HALF_UP
for ns, ms, rnd in (
# nanoseconds
(1, 0, FLOOR),
(1, 1, CEILING),
+ (1, 0, HALF_UP),
(-1, 0, FLOOR),
(-1, -1, CEILING),
+ (-1, 0, HALF_UP),
# seconds + nanoseconds
(1234 * MS_TO_NS + 1, 1234, FLOOR),
(1234 * MS_TO_NS + 1, 1235, CEILING),
+ (1234 * MS_TO_NS + 1, 1234, HALF_UP),
(-1234 * MS_TO_NS - 1, -1234, FLOOR),
(-1234 * MS_TO_NS - 1, -1235, CEILING),
+ (-1234 * MS_TO_NS - 1, -1234, HALF_UP),
+
+ # half up
+ (499999, 0, HALF_UP),
+ (499999, 0, HALF_UP),
+ (500000, 1, HALF_UP),
+ (999999, 1, HALF_UP),
+ (-499999, 0, HALF_UP),
+ (-500000, -1, HALF_UP),
+ (-500001, -1, HALF_UP),
+ (-999999, -1, HALF_UP),
):
with self.subTest(nanoseconds=ns, milliseconds=ms, round=rnd):
self.assertEqual(PyTime_AsMilliseconds(ns, rnd), ms)
FLOOR = _PyTime.ROUND_FLOOR
CEILING = _PyTime.ROUND_CEILING
+ HALF_UP = _PyTime.ROUND_HALF_UP
for ns, ms, rnd in (
# nanoseconds
(1, 0, FLOOR),
(1, 1, CEILING),
+ (1, 0, HALF_UP),
(-1, 0, FLOOR),
(-1, -1, CEILING),
+ (-1, 0, HALF_UP),
# seconds + nanoseconds
(1234 * US_TO_NS + 1, 1234, FLOOR),
(1234 * US_TO_NS + 1, 1235, CEILING),
+ (1234 * US_TO_NS + 1, 1234, HALF_UP),
(-1234 * US_TO_NS - 1, -1234, FLOOR),
(-1234 * US_TO_NS - 1, -1235, CEILING),
+ (-1234 * US_TO_NS - 1, -1234, HALF_UP),
+
+ # half up
+ (1499, 1, HALF_UP),
+ (1500, 2, HALF_UP),
+ (1501, 2, HALF_UP),
+ (-1499, -1, HALF_UP),
+ (-1500, -2, HALF_UP),
+ (-1501, -2, HALF_UP),
):
with self.subTest(nanoseconds=ns, milliseconds=ms, round=rnd):
self.assertEqual(PyTime_AsMicroseconds(ns, rnd), ms)
#endif
}
+static double
+_PyTime_RoundHalfUp(double x)
+{
+ if (x >= 0.0)
+ x = floor(x + 0.5);
+ else
+ x = ceil(x - 0.5);
+ return x;
+}
+
+
static int
_PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator,
double denominator, _PyTime_round_t round)
}
floatpart *= denominator;
- if (round == _PyTime_ROUND_CEILING) {
+ if (round == _PyTime_ROUND_HALF_UP)
+ floatpart = _PyTime_RoundHalfUp(floatpart);
+ else if (round == _PyTime_ROUND_CEILING) {
floatpart = ceil(floatpart);
if (floatpart >= denominator) {
floatpart = 0.0;
double d, intpart, err;
d = PyFloat_AsDouble(obj);
- if (round == _PyTime_ROUND_CEILING)
+ if (round == _PyTime_ROUND_HALF_UP)
+ d = _PyTime_RoundHalfUp(d);
+ else if (round == _PyTime_ROUND_CEILING)
d = ceil(d);
else
d = floor(d);
d = value;
d *= to_nanoseconds;
- if (round == _PyTime_ROUND_CEILING)
+ if (round == _PyTime_ROUND_HALF_UP)
+ d = _PyTime_RoundHalfUp(d);
+ else if (round == _PyTime_ROUND_CEILING)
d = ceil(d);
else
d = floor(d);
_PyTime_Divide(_PyTime_t t, _PyTime_t k, _PyTime_round_t round)
{
assert(k > 1);
- if (round == _PyTime_ROUND_CEILING) {
+ if (round == _PyTime_ROUND_HALF_UP) {
+ _PyTime_t x, r;
+ x = t / k;
+ r = t % k;
+ if (Py_ABS(r) >= k / 2) {
+ if (t >= 0)
+ x++;
+ else
+ x--;
+ }
+ return x;
+ }
+ else if (round == _PyTime_ROUND_CEILING) {
if (t >= 0)
return (t + k - 1) / k;
else
_PyTime_AsTimeval_impl(_PyTime_t t, struct timeval *tv, _PyTime_round_t round,
int raise)
{
+ const long k = US_TO_NS;
_PyTime_t secs, ns;
int res = 0;
+ int usec;
secs = t / SEC_TO_NS;
ns = t % SEC_TO_NS;
res = -1;
#endif
- if (round == _PyTime_ROUND_CEILING)
- tv->tv_usec = (int)((ns + US_TO_NS - 1) / US_TO_NS);
+ if (round == _PyTime_ROUND_HALF_UP) {
+ _PyTime_t r;
+ usec = (int)(ns / k);
+ r = ns % k;
+ if (Py_ABS(r) >= k / 2) {
+ if (ns >= 0)
+ usec++;
+ else
+ usec--;
+ }
+ }
+ else if (round == _PyTime_ROUND_CEILING)
+ usec = (int)((ns + k - 1) / k);
else
- tv->tv_usec = (int)(ns / US_TO_NS);
+ usec = (int)(ns / k);
- if (tv->tv_usec >= SEC_TO_US) {
- tv->tv_usec -= SEC_TO_US;
+ if (usec >= SEC_TO_US) {
+ usec -= SEC_TO_US;
tv->tv_sec += 1;
}
if (res && raise)
_PyTime_overflow();
- assert(0 <= tv->tv_usec && tv->tv_usec <= 999999);
+ assert(0 <= usec && usec <= 999999);
+
+ tv->tv_usec = usec;
return res;
}