Issue #25155: Add _PyTime_AsTimevalTime_t() function

On Windows, the tv_sec field of the timeval structure has the type C long,
whereas it has the type C time_t on all other platforms. A C long has a size of
32 bits (signed inter, 1 bit for the sign, 31 bits for the value) which is not
enough to store an Epoch timestamp after the year 2038.

Add the _PyTime_AsTimevalTime_t() function written for datetime.datetime.now():
convert a _PyTime_t timestamp to a (secs, us) tuple where secs type is time_t.
It allows to support dates after the year 2038 on Windows.

Enhance also _PyTime_AsTimeval_impl() to detect overflow on the number of
seconds when rounding the number of microseconds.
This commit is contained in:
Victor Stinner 2015-09-18 13:23:02 +02:00
parent 1bd0b54c74
commit 1e2b6882fc
3 changed files with 80 additions and 26 deletions

View file

@ -120,6 +120,18 @@ PyAPI_FUNC(int) _PyTime_AsTimeval_noraise(_PyTime_t t,
struct timeval *tv, struct timeval *tv,
_PyTime_round_t round); _PyTime_round_t round);
/* Convert a timestamp to a number of seconds (secs) and microseconds (us).
us is always positive. This function is similar to _PyTime_AsTimeval()
except that secs is always a time_t type, whereas the timeval structure
uses a C long for tv_sec on Windows.
Raise an exception and return -1 if the conversion overflowed,
return 0 on success. */
PyAPI_FUNC(int) _PyTime_AsTimevalTime_t(
_PyTime_t t,
time_t *secs,
int *us,
_PyTime_round_t round);
#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_KQUEUE) #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_KQUEUE)
/* Convert a timestamp to a timespec structure (nanosecond resolution). /* Convert a timestamp to a timespec structure (nanosecond resolution).
tv_nsec is always positive. tv_nsec is always positive.

View file

@ -4117,13 +4117,14 @@ static PyObject *
datetime_best_possible(PyObject *cls, TM_FUNC f, PyObject *tzinfo) datetime_best_possible(PyObject *cls, TM_FUNC f, PyObject *tzinfo)
{ {
_PyTime_t ts = _PyTime_GetSystemClock(); _PyTime_t ts = _PyTime_GetSystemClock();
struct timeval tv; time_t secs;
int us;
if (_PyTime_AsTimeval(ts, &tv, _PyTime_ROUND_FLOOR) < 0) if (_PyTime_AsTimevalTime_t(ts, &secs, &us, _PyTime_ROUND_FLOOR) < 0)
return NULL; return NULL;
assert(0 <= tv.tv_usec && tv.tv_usec <= 999999); assert(0 <= us && us <= 999999);
return datetime_from_timet_and_us(cls, f, tv.tv_sec, tv.tv_usec, tzinfo); return datetime_from_timet_and_us(cls, f, secs, us, tzinfo);
} }
/*[clinic input] /*[clinic input]

View file

@ -417,54 +417,95 @@ _PyTime_AsMicroseconds(_PyTime_t t, _PyTime_round_t round)
} }
static int static int
_PyTime_AsTimeval_impl(_PyTime_t t, struct timeval *tv, _PyTime_round_t round, _PyTime_AsTimeval_impl(_PyTime_t t, _PyTime_t *p_secs, int *p_us,
int raise) _PyTime_round_t round)
{ {
_PyTime_t secs, ns; _PyTime_t secs, ns;
int res = 0;
int usec; int usec;
int res = 0;
secs = t / SEC_TO_NS; secs = t / SEC_TO_NS;
ns = t % SEC_TO_NS; ns = t % SEC_TO_NS;
usec = (int)_PyTime_Divide(ns, US_TO_NS, round);
if (usec < 0) {
usec += SEC_TO_US;
if (secs != _PyTime_MIN)
secs -= 1;
else
res = -1;
}
else if (usec >= SEC_TO_US) {
usec -= SEC_TO_US;
if (secs != _PyTime_MAX)
secs += 1;
else
res = -1;
}
assert(0 <= usec && usec < SEC_TO_US);
*p_secs = secs;
*p_us = usec;
return res;
}
static int
_PyTime_AsTimevalStruct_impl(_PyTime_t t, struct timeval *tv,
_PyTime_round_t round, int raise)
{
_PyTime_t secs;
int us;
int res;
res = _PyTime_AsTimeval_impl(t, &secs, &us, round);
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
tv->tv_sec = (long)secs; tv->tv_sec = (long)secs;
#else #else
tv->tv_sec = secs; tv->tv_sec = secs;
#endif #endif
if ((_PyTime_t)tv->tv_sec != secs) tv->tv_usec = us;
res = -1;
usec = (int)_PyTime_Divide(ns, US_TO_NS, round); if (res < 0 || (_PyTime_t)tv->tv_sec != secs) {
if (usec < 0) { if (raise)
usec += SEC_TO_US;
tv->tv_sec -= 1;
}
else if (usec >= SEC_TO_US) {
usec -= SEC_TO_US;
tv->tv_sec += 1;
}
assert(0 <= usec && usec < SEC_TO_US);
tv->tv_usec = usec;
if (res && raise)
error_time_t_overflow(); error_time_t_overflow();
return res; return -1;
}
return 0;
} }
int int
_PyTime_AsTimeval(_PyTime_t t, struct timeval *tv, _PyTime_round_t round) _PyTime_AsTimeval(_PyTime_t t, struct timeval *tv, _PyTime_round_t round)
{ {
return _PyTime_AsTimeval_impl(t, tv, round, 1); return _PyTime_AsTimevalStruct_impl(t, tv, round, 1);
} }
int int
_PyTime_AsTimeval_noraise(_PyTime_t t, struct timeval *tv, _PyTime_round_t round) _PyTime_AsTimeval_noraise(_PyTime_t t, struct timeval *tv, _PyTime_round_t round)
{ {
return _PyTime_AsTimeval_impl(t, tv, round, 0); return _PyTime_AsTimevalStruct_impl(t, tv, round, 0);
} }
int
_PyTime_AsTimevalTime_t(_PyTime_t t, time_t *p_secs, int *us,
_PyTime_round_t round)
{
_PyTime_t secs;
int res;
res = _PyTime_AsTimeval_impl(t, &secs, us, round);
*p_secs = secs;
if (res < 0 || (_PyTime_t)*p_secs != secs) {
error_time_t_overflow();
return -1;
}
return 0;
}
#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_KQUEUE) #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_KQUEUE)
int int
_PyTime_AsTimespec(_PyTime_t t, struct timespec *ts) _PyTime_AsTimespec(_PyTime_t t, struct timespec *ts)