Skip to content

Commit 5d644dd

Browse files
committed
SF bug 661086: datetime.today() truncates microseconds.
On Windows, it was very common to get microsecond values (out of .today() and .now()) of the form 480999, i.e. with three trailing nines. The platform precision is .001 seconds, and fp rounding errors account for the rest. Under the covers, that 480999 started life as the fractional part of a timestamp, like .4809999978. Rounding that times 1e6 cures the irritation. Confession: the platform precision isn't really .001 seconds. It's usually worse. What actually happens is that MS rounds a cruder value to a multiple of .001, and that suffers its own rounding errors. A tiny bit of refactoring added a new internal utility to round doubles.
1 parent e555346 commit 5d644dd

1 file changed

Lines changed: 16 additions & 7 deletions

File tree

Modules/datetimemodule.c

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,19 @@ divmod(int x, int y, int *r)
120120
return quo;
121121
}
122122

123+
/* Round a double to the nearest long. |x| must be small enough to fit
124+
* in a C long; this is not checked.
125+
*/
126+
static long
127+
round_to_long(double x)
128+
{
129+
if (x >= 0.0)
130+
x = floor(x + 0.5);
131+
else
132+
x = ceil(x - 0.5);
133+
return (long)x;
134+
}
135+
123136
/* ---------------------------------------------------------------------------
124137
* General calendrical helper functions
125138
*/
@@ -1905,12 +1918,7 @@ delta_new(PyTypeObject *type, PyObject *args, PyObject *kw)
19051918
}
19061919
if (leftover_us) {
19071920
/* Round to nearest whole # of us, and add into x. */
1908-
PyObject *temp;
1909-
if (leftover_us >= 0.0)
1910-
leftover_us = floor(leftover_us + 0.5);
1911-
else
1912-
leftover_us = ceil(leftover_us - 0.5);
1913-
temp = PyLong_FromDouble(leftover_us);
1921+
PyObject *temp = PyLong_FromLong(round_to_long(leftover_us));
19141922
if (temp == NULL) {
19151923
Py_DECREF(x);
19161924
goto Done;
@@ -2858,7 +2866,8 @@ static PyObject *
28582866
datetime_from_timestamp(PyObject *cls, TM_FUNC f, double timestamp)
28592867
{
28602868
time_t timet = (time_t)timestamp;
2861-
int us = (int)((timestamp - (double)timet) * 1e6);
2869+
double fraction = timestamp - (double)timet;
2870+
int us = (int)round_to_long(fraction * 1e6);
28622871

28632872
return datetime_from_timet_and_us(cls, f, timet, us);
28642873
}

0 commit comments

Comments
 (0)