Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 02937aa

Browse files
committed
Issue #22117: Add the new _PyTime_ROUND_FLOOR rounding method for the datetime
module. time.clock_settime() now uses this rounding method instead of _PyTime_ROUND_DOWN to handle correctly dates before 1970.
1 parent b3b4544 commit 02937aa

5 files changed

Lines changed: 43 additions & 56 deletions

File tree

Include/pytime.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,12 @@ PyAPI_FUNC(void) _PyTime_gettimeofday(_PyTime_timeval *tp);
3838
typedef enum {
3939
/* Round towards zero. */
4040
_PyTime_ROUND_DOWN=0,
41-
/* Round away from zero. */
42-
_PyTime_ROUND_UP
41+
/* Round away from zero.
42+
For example, used for timeout to wait "at least" N seconds. */
43+
_PyTime_ROUND_UP,
44+
/* Round towards minus infinity (-inf).
45+
For example, used to read a clock. */
46+
_PyTime_ROUND_FLOOR
4347
} _PyTime_round_t;
4448

4549
/* Convert a number of seconds, int or float, to time_t. */
@@ -81,6 +85,9 @@ PyAPI_FUNC(int) _PyTime_Init(void);
8185
/****************** NEW _PyTime_t API **********************/
8286

8387
#ifdef PY_INT64_T
88+
/* _PyTime_t: Python timestamp with subsecond precision. It can be used to
89+
store a duration, and so indirectly a date (related to another date, like
90+
UNIX epoch). */
8491
typedef PY_INT64_T _PyTime_t;
8592
#define _PyTime_MIN PY_LLONG_MIN
8693
#define _PyTime_MAX PY_LLONG_MAX

Lib/test/test_time.py

Lines changed: 18 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,13 @@ class _PyTime(enum.IntEnum):
2828
ROUND_DOWN = 0
2929
# Round away from zero
3030
ROUND_UP = 1
31+
# Round towards -Infinity
32+
ROUND_FLOOR = 2
3133

32-
ALL_ROUNDING_METHODS = (_PyTime.ROUND_UP, _PyTime.ROUND_DOWN)
34+
ALL_ROUNDING_METHODS = (
35+
_PyTime.ROUND_UP,
36+
_PyTime.ROUND_DOWN,
37+
_PyTime.ROUND_FLOOR)
3338

3439

3540
class TimeTestCase(unittest.TestCase):
@@ -631,53 +636,6 @@ def test_time_t(self):
631636
self.assertRaises(OverflowError,
632637
pytime_object_to_time_t, invalid, rnd)
633638

634-
@support.cpython_only
635-
def test_timeval(self):
636-
from _testcapi import pytime_object_to_timeval
637-
for obj, timeval, rnd in (
638-
# Round towards zero
639-
(0, (0, 0), _PyTime.ROUND_DOWN),
640-
(-1, (-1, 0), _PyTime.ROUND_DOWN),
641-
(-1.0, (-1, 0), _PyTime.ROUND_DOWN),
642-
(1e-6, (0, 1), _PyTime.ROUND_DOWN),
643-
(1e-7, (0, 0), _PyTime.ROUND_DOWN),
644-
(-1e-6, (-1, 999999), _PyTime.ROUND_DOWN),
645-
(-1e-7, (-1, 999999), _PyTime.ROUND_DOWN),
646-
(-1.2, (-2, 800000), _PyTime.ROUND_DOWN),
647-
(0.9999999, (0, 999999), _PyTime.ROUND_DOWN),
648-
(0.0000041, (0, 4), _PyTime.ROUND_DOWN),
649-
(1.1234560, (1, 123456), _PyTime.ROUND_DOWN),
650-
(1.1234569, (1, 123456), _PyTime.ROUND_DOWN),
651-
(-0.0000040, (-1, 999996), _PyTime.ROUND_DOWN),
652-
(-0.0000041, (-1, 999995), _PyTime.ROUND_DOWN),
653-
(-1.1234560, (-2, 876544), _PyTime.ROUND_DOWN),
654-
(-1.1234561, (-2, 876543), _PyTime.ROUND_DOWN),
655-
# Round away from zero
656-
(0, (0, 0), _PyTime.ROUND_UP),
657-
(-1, (-1, 0), _PyTime.ROUND_UP),
658-
(-1.0, (-1, 0), _PyTime.ROUND_UP),
659-
(1e-6, (0, 1), _PyTime.ROUND_UP),
660-
(1e-7, (0, 1), _PyTime.ROUND_UP),
661-
(-1e-6, (-1, 999999), _PyTime.ROUND_UP),
662-
(-1e-7, (-1, 999999), _PyTime.ROUND_UP),
663-
(-1.2, (-2, 800000), _PyTime.ROUND_UP),
664-
(0.9999999, (1, 0), _PyTime.ROUND_UP),
665-
(0.0000041, (0, 5), _PyTime.ROUND_UP),
666-
(1.1234560, (1, 123457), _PyTime.ROUND_UP),
667-
(1.1234569, (1, 123457), _PyTime.ROUND_UP),
668-
(-0.0000040, (-1, 999996), _PyTime.ROUND_UP),
669-
(-0.0000041, (-1, 999995), _PyTime.ROUND_UP),
670-
(-1.1234560, (-2, 876544), _PyTime.ROUND_UP),
671-
(-1.1234561, (-2, 876543), _PyTime.ROUND_UP),
672-
):
673-
with self.subTest(obj=obj, round=rnd, timeval=timeval):
674-
self.assertEqual(pytime_object_to_timeval(obj, rnd), timeval)
675-
676-
rnd = _PyTime.ROUND_DOWN
677-
for invalid in self.invalid_values:
678-
self.assertRaises(OverflowError,
679-
pytime_object_to_timeval, invalid, rnd)
680-
681639
@support.cpython_only
682640
def test_timespec(self):
683641
from _testcapi import pytime_object_to_timespec
@@ -835,24 +793,31 @@ def test_FromSecondsObject(self):
835793
# Conversion giving different results depending on the rounding method
836794
UP = _PyTime.ROUND_UP
837795
DOWN = _PyTime.ROUND_DOWN
796+
FLOOR = _PyTime.ROUND_FLOOR
838797
for obj, ts, rnd in (
839798
# close to zero
840799
( 1e-10, 1, UP),
841800
( 1e-10, 0, DOWN),
801+
( 1e-10, 0, FLOOR),
842802
(-1e-10, 0, DOWN),
843803
(-1e-10, -1, UP),
804+
(-1e-10, -1, FLOOR),
844805

845806
# test rounding of the last nanosecond
846807
( 1.1234567899, 1123456790, UP),
847808
( 1.1234567899, 1123456789, DOWN),
809+
( 1.1234567899, 1123456789, FLOOR),
848810
(-1.1234567899, -1123456789, DOWN),
849811
(-1.1234567899, -1123456790, UP),
812+
(-1.1234567899, -1123456790, FLOOR),
850813

851814
# close to 1 second
852815
( 0.9999999999, 1000000000, UP),
853816
( 0.9999999999, 999999999, DOWN),
817+
( 0.9999999999, 999999999, FLOOR),
854818
(-0.9999999999, -999999999, DOWN),
855819
(-0.9999999999, -1000000000, UP),
820+
(-0.9999999999, -1000000000, FLOOR),
856821
):
857822
with self.subTest(obj=obj, round=rnd, timestamp=ts):
858823
self.assertEqual(PyTime_FromSecondsObject(obj, rnd), ts)
@@ -924,18 +889,23 @@ def test_timeval(self):
924889

925890
UP = _PyTime.ROUND_UP
926891
DOWN = _PyTime.ROUND_DOWN
892+
FLOOR = _PyTime.ROUND_FLOOR
927893
for ns, tv, rnd in (
928894
# nanoseconds
929895
(1, (0, 1), UP),
930896
(1, (0, 0), DOWN),
897+
(1, (0, 0), FLOOR),
931898
(-1, (0, 0), DOWN),
932899
(-1, (-1, 999999), UP),
900+
(-1, (-1, 999999), FLOOR),
933901

934902
# seconds + nanoseconds
935903
(1234567001, (1, 234568), UP),
936904
(1234567001, (1, 234567), DOWN),
905+
(1234567001, (1, 234567), FLOOR),
937906
(-1234567001, (-2, 765433), DOWN),
938907
(-1234567001, (-2, 765432), UP),
908+
(-1234567001, (-2, 765432), FLOOR),
939909
):
940910
with self.subTest(nanoseconds=ns, timeval=tv, round=rnd):
941911
self.assertEqual(PyTime_AsTimeval(ns, rnd), tv)

Modules/_testcapimodule.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2634,7 +2634,8 @@ run_in_subinterp(PyObject *self, PyObject *args)
26342634
static int
26352635
check_time_rounding(int round)
26362636
{
2637-
if (round != _PyTime_ROUND_DOWN && round != _PyTime_ROUND_UP) {
2637+
if (round != _PyTime_ROUND_DOWN && round != _PyTime_ROUND_UP
2638+
&& round != _PyTime_ROUND_FLOOR) {
26382639
PyErr_SetString(PyExc_ValueError, "invalid rounding");
26392640
return -1;
26402641
}

Modules/timemodule.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ time_clock_settime(PyObject *self, PyObject *args)
173173
if (!PyArg_ParseTuple(args, "iO:clock_settime", &clk_id, &obj))
174174
return NULL;
175175

176-
if (_PyTime_FromSecondsObject(&t, obj, _PyTime_ROUND_DOWN) < 0)
176+
if (_PyTime_FromSecondsObject(&t, obj, _PyTime_ROUND_FLOOR) < 0)
177177
return NULL;
178178

179179
if (_PyTime_AsTimespec(t, &tp) == -1)
@@ -322,7 +322,7 @@ parse_time_t_args(PyObject *args, char *format, time_t *pwhen)
322322
whent = time(NULL);
323323
}
324324
else {
325-
if (_PyTime_ObjectToTime_t(ot, &whent, _PyTime_ROUND_DOWN) == -1)
325+
if (_PyTime_ObjectToTime_t(ot, &whent, _PyTime_ROUND_FLOOR) == -1)
326326
return 0;
327327
}
328328
*pwhen = whent;

Python/pytime.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,14 @@ _PyTime_overflow(void)
260260
"timestamp too large to convert to C _PyTime_t");
261261
}
262262

263+
int
264+
_PyTime_RoundTowardsInfinity(int is_neg, _PyTime_round_t round)
265+
{
266+
if (round == _PyTime_ROUND_FLOOR)
267+
return 0;
268+
return ((round == _PyTime_ROUND_UP) ^ is_neg);
269+
}
270+
263271
_PyTime_t
264272
_PyTime_FromNanoseconds(PY_LONG_LONG ns)
265273
{
@@ -314,7 +322,7 @@ _PyTime_FromSecondsObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round)
314322
d = PyFloat_AsDouble(obj);
315323
d *= 1e9;
316324

317-
if ((round == _PyTime_ROUND_UP) ^ (d < 0))
325+
if (_PyTime_RoundTowardsInfinity(d < 0, round))
318326
d = ceil(d);
319327
else
320328
d = floor(d);
@@ -380,7 +388,7 @@ _PyTime_Multiply(_PyTime_t t, unsigned int multiply, _PyTime_round_t round)
380388
_PyTime_t k;
381389
if (multiply < SEC_TO_NS) {
382390
k = SEC_TO_NS / multiply;
383-
if (round == _PyTime_ROUND_UP)
391+
if (_PyTime_RoundTowardsInfinity(t < 0, round))
384392
return (t + k - 1) / k;
385393
else
386394
return t / k;
@@ -397,6 +405,7 @@ _PyTime_AsMilliseconds(_PyTime_t t, _PyTime_round_t round)
397405
return _PyTime_Multiply(t, 1000, round);
398406
}
399407

408+
/* FIXME: write unit tests */
400409
_PyTime_t
401410
_PyTime_AsMicroseconds(_PyTime_t t, _PyTime_round_t round)
402411
{
@@ -439,7 +448,7 @@ _PyTime_AsTimeval(_PyTime_t t, struct timeval *tv, _PyTime_round_t round)
439448
res = -1;
440449
#endif
441450

442-
if ((round == _PyTime_ROUND_UP) ^ (tv->tv_sec < 0))
451+
if (_PyTime_RoundTowardsInfinity(tv->tv_sec < 0, round))
443452
tv->tv_usec = (int)((ns + US_TO_NS - 1) / US_TO_NS);
444453
else
445454
tv->tv_usec = (int)(ns / US_TO_NS);

0 commit comments

Comments
 (0)