summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2015-09-18 11:36:17 (GMT)
committerVictor Stinner <victor.stinner@gmail.com>2015-09-18 11:36:17 (GMT)
commit9a8b177e60cc5cc6d5105519c0df8fb185211e1d (patch)
tree83036f077d2267d3c60f172242de4ee8892a5323
parent4b352171d2b0a4a63cd711df9ebe840419137fa2 (diff)
downloadcpython-9a8b177e60cc5cc6d5105519c0df8fb185211e1d.zip
cpython-9a8b177e60cc5cc6d5105519c0df8fb185211e1d.tar.gz
cpython-9a8b177e60cc5cc6d5105519c0df8fb185211e1d.tar.bz2
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.
-rw-r--r--Include/pytime.h12
-rw-r--r--Modules/_datetimemodule.c9
-rw-r--r--Python/pytime.c99
3 files changed, 78 insertions, 42 deletions
diff --git a/Include/pytime.h b/Include/pytime.h
index 027c3d8..494322c 100644
--- a/Include/pytime.h
+++ b/Include/pytime.h
@@ -117,6 +117,18 @@ PyAPI_FUNC(int) _PyTime_AsTimeval_noraise(_PyTime_t t,
struct timeval *tv,
_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)
/* Convert a timestamp to a timespec structure (nanosecond resolution).
tv_nsec is always positive.
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c
index 7e4be5b..662dcbc 100644
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -4113,13 +4113,14 @@ static PyObject *
datetime_best_possible(PyObject *cls, TM_FUNC f, PyObject *tzinfo)
{
_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;
- 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]
diff --git a/Python/pytime.c b/Python/pytime.c
index 77db204..85e1ca8 100644
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -331,69 +331,92 @@ _PyTime_AsMicroseconds(_PyTime_t t, _PyTime_round_t round)
}
static int
-_PyTime_AsTimeval_impl(_PyTime_t t, struct timeval *tv, _PyTime_round_t round,
- int raise)
+_PyTime_AsTimeval_impl(_PyTime_t t, _PyTime_t *p_secs, int *p_us,
+ _PyTime_round_t round)
{
_PyTime_t secs, ns;
+ int usec;
int res = 0;
secs = t / SEC_TO_NS;
ns = t % SEC_TO_NS;
- if (ns < 0) {
- ns += SEC_TO_NS;
- secs -= 1;
- }
-#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;
+ 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 (secs < LONG_MIN) {
- secs = LONG_MIN;
- res = -1;
+ else if (usec >= SEC_TO_US) {
+ usec -= SEC_TO_US;
+ if (secs != _PyTime_MAX)
+ secs += 1;
+ else
+ res = -1;
}
-#endif
+ 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
tv->tv_sec = (long)secs;
#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;
- if ((_PyTime_t)tv->tv_sec != secs)
- res = -1;
#endif
+ tv->tv_usec = us;
- if (round == _PyTime_ROUND_CEILING)
- tv->tv_usec = (int)((ns + US_TO_NS - 1) / US_TO_NS);
- else
- tv->tv_usec = (int)(ns / US_TO_NS);
-
- if (tv->tv_usec >= SEC_TO_US) {
- tv->tv_usec -= SEC_TO_US;
- tv->tv_sec += 1;
+ if (res < 0 || (_PyTime_t)tv->tv_sec != secs) {
+ if (raise)
+ error_time_t_overflow();
+ return -1;
}
-
- if (res && raise)
- _PyTime_overflow();
-
- assert(0 <= tv->tv_usec && tv->tv_usec <= 999999);
- return res;
+ return 0;
}
int
_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
_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)