mirror of https://github.com/python/cpython.git
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:
parent
4b352171d2
commit
9a8b177e60
|
@ -117,6 +117,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.
|
||||||
|
|
|
@ -4113,13 +4113,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]
|
||||||
|
|
|
@ -331,69 +331,92 @@ _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 usec;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
|
||||||
secs = t / SEC_TO_NS;
|
secs = t / SEC_TO_NS;
|
||||||
ns = t % SEC_TO_NS;
|
ns = t % SEC_TO_NS;
|
||||||
if (ns < 0) {
|
|
||||||
ns += SEC_TO_NS;
|
usec = (int)_PyTime_Divide(ns, US_TO_NS, round);
|
||||||
secs -= 1;
|
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
|
||||||
/* On Windows, timeval.tv_sec is a long (32 bit),
|
|
||||||
whereas time_t can be 64-bit. */
|
|
||||||
assert(sizeof(tv->tv_sec) == sizeof(long));
|
|
||||||
#if SIZEOF_TIME_T > SIZEOF_LONG
|
|
||||||
if (secs > LONG_MAX) {
|
|
||||||
secs = LONG_MAX;
|
|
||||||
res = -1;
|
|
||||||
}
|
|
||||||
else if (secs < LONG_MIN) {
|
|
||||||
secs = LONG_MIN;
|
|
||||||
res = -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
tv->tv_sec = (long)secs;
|
tv->tv_sec = (long)secs;
|
||||||
#else
|
#else
|
||||||
/* On OpenBSD 5.4, timeval.tv_sec is a long.
|
|
||||||
Example: long is 64-bit, whereas time_t is 32-bit. */
|
|
||||||
tv->tv_sec = secs;
|
tv->tv_sec = secs;
|
||||||
if ((_PyTime_t)tv->tv_sec != secs)
|
|
||||||
res = -1;
|
|
||||||
#endif
|
#endif
|
||||||
|
tv->tv_usec = us;
|
||||||
|
|
||||||
if (round == _PyTime_ROUND_CEILING)
|
if (res < 0 || (_PyTime_t)tv->tv_sec != secs) {
|
||||||
tv->tv_usec = (int)((ns + US_TO_NS - 1) / US_TO_NS);
|
if (raise)
|
||||||
else
|
error_time_t_overflow();
|
||||||
tv->tv_usec = (int)(ns / US_TO_NS);
|
return -1;
|
||||||
|
|
||||||
if (tv->tv_usec >= SEC_TO_US) {
|
|
||||||
tv->tv_usec -= SEC_TO_US;
|
|
||||||
tv->tv_sec += 1;
|
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
if (res && raise)
|
|
||||||
_PyTime_overflow();
|
|
||||||
|
|
||||||
assert(0 <= tv->tv_usec && tv->tv_usec <= 999999);
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
|
Loading…
Reference in New Issue