[Python-checkins] cpython (3.5): Issue #25155: Add _PyTime_AsTimevalTime_t() function

victor.stinner python-checkins at python.org
Fri Sep 18 13:56:08 CEST 2015


https://hg.python.org/cpython/rev/4ca99a0a18e4
changeset:   98041:4ca99a0a18e4
branch:      3.5
parent:      98038:f347ea4391f3
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Fri Sep 18 13:36:17 2015 +0200
summary:
  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.

files:
  Include/pytime.h          |  12 +++
  Modules/_datetimemodule.c |  13 +-
  Python/pytime.c           |  99 ++++++++++++++++----------
  3 files changed, 80 insertions(+), 44 deletions(-)


diff --git a/Include/pytime.h b/Include/pytime.h
--- a/Include/pytime.h
+++ b/Include/pytime.h
@@ -117,6 +117,18 @@
     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
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -4113,13 +4113,14 @@
 datetime_best_possible(PyObject *cls, TM_FUNC f, PyObject *tzinfo)
 {
     _PyTime_t ts = _PyTime_GetSystemClock();
-    struct timeval tv;
-
-    if (_PyTime_AsTimeval(ts, &tv, _PyTime_ROUND_FLOOR) < 0)
+    time_t secs;
+    int us;
+
+    if (_PyTime_AsTimevalTime_t(ts, &secs, &us, _PyTime_ROUND_FLOOR) < 0)
         return NULL;
-    assert(0 <= tv.tv_usec && tv.tv_usec <= 999999);
-
-    return datetime_from_timet_and_us(cls, f, tv.tv_sec, tv.tv_usec, tzinfo);
+    assert(0 <= us && us <= 999999);
+
+    return datetime_from_timet_and_us(cls, f, secs, us, tzinfo);
 }
 
 /*[clinic input]
diff --git a/Python/pytime.c b/Python/pytime.c
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -331,69 +331,92 @@
 }
 
 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;
+
+    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
-    /* 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;
 #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)

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list