diff --git a/Doc/c-api/time.rst b/Doc/c-api/time.rst
new file mode 100644
index 000000000000000..adb122db576d9bd
--- /dev/null
+++ b/Doc/c-api/time.rst
@@ -0,0 +1,103 @@
+.. highlight:: c
+
+PyTime C API
+============
+
+.. versionadded:: 3.13
+
+PyTime API
+----------
+
+The PyTime_t API is written to use timestamp and timeout values stored in
+various formats and to read clocks with a resolution of one nanosecond.
+
+The :c:type:`PyTime_t` type is signed to support negative timestamps. The
+supported range is around [-292.3 years; +292.3 years]. Using the Unix epoch
+(January 1st, 1970) as reference, the supported date range is around
+[1677-09-21; 2262-04-11].
+
+
+Types
+-----
+
+.. c:type:: PyTime_t
+
+ Timestamp type with subsecond precision: 64-bit signed integer.
+
+ This type can be used to store a duration. Indirectly, it can be used to
+ store a date relative to a reference date, such as the UNIX epoch.
+
+
+Constants
+---------
+
+.. c:var:: PyTime_t PyTime_MIN
+
+ Minimum value of the :c:type:`PyTime_t` type.
+
+ :c:var:`PyTime_MIN` nanoseconds is around -292.3 years.
+
+.. c:var:: PyTime_t PyTime_MAX
+
+ Maximum value of the :c:type:`PyTime_t` type.
+
+ :c:var:`PyTime_MAX` nanoseconds is around +292.3 years.
+
+
+Functions
+---------
+
+.. c:function:: double PyTime_AsSecondsDouble(PyTime_t t)
+
+ Convert a timestamp to a number of seconds as a C :c:expr:`double`.
+
+ The function cannot fail.
+
+
+.. c:function:: PyTime_t PyTime_Monotonic(void)
+
+ Get the monotonic clock: clock that cannot go backwards.
+
+ The monotonic clock is not affected by system clock updates. The reference
+ point of the returned value is undefined, so that only the difference
+ between the results of consecutive calls is valid.
+
+ If reading the clock fails, silently ignore the error and return 0.
+
+ On integer overflow, silently ignore the overflow and clamp the clock to
+ the ``[PyTime_MIN; PyTime_MAX]`` range.
+
+ See also the :func:`time.monotonic` function.
+
+
+.. c:function:: PyTime_t PyTime_PerfCounter(void)
+
+ Get the performance counter: clock with the highest available resolution to
+ measure a short duration.
+
+ The performance counter does include time elapsed during sleep and is
+ system-wide. The reference point of the returned value is undefined, so that
+ only the difference between the results of two calls is valid.
+
+ If reading the clock fails, silently ignore the error and return 0.
+
+ On integer overflow, silently ignore the overflow and clamp the clock to
+ the ``[PyTime_MIN; PyTime_MAX]`` range.
+
+ See also the :func:`time.perf_counter` function.
+
+
+.. c:function:: PyTime_t PyTime_Time(void)
+
+ Get the system clock.
+
+ The system clock can be changed automatically (e.g. by a NTP daemon) or
+ manually by the system administrator. So it can also go backward. Use
+ :c:func:`PyTime_Monotonic` to use a monotonic clock.
+
+ If reading the clock fails, silently ignore the error and return ``0``.
+
+ On integer overflow, silently ignore the overflow and clamp the clock to
+ the ``[PyTime_MIN; PyTime_MAX]`` range.
+
+ See also the :func:`time.time` function.
diff --git a/Doc/c-api/utilities.rst b/Doc/c-api/utilities.rst
index 48ae54acebe8879..9d0abf440f791d4 100644
--- a/Doc/c-api/utilities.rst
+++ b/Doc/c-api/utilities.rst
@@ -20,4 +20,5 @@ and parsing function arguments and constructing Python values from C values.
hash.rst
reflection.rst
codec.rst
+ time.rst
perfmaps.rst
diff --git a/Doc/conf.py b/Doc/conf.py
index 0d7c0b553eaa741..0e57b9496106331 100644
--- a/Doc/conf.py
+++ b/Doc/conf.py
@@ -119,11 +119,14 @@
('c:type', 'wchar_t'),
('c:type', '__int64'),
('c:type', 'unsigned __int64'),
+ ('c:type', 'double'),
# Standard C structures
('c:struct', 'in6_addr'),
('c:struct', 'in_addr'),
('c:struct', 'stat'),
('c:struct', 'statvfs'),
+ ('c:struct', 'timeval'),
+ ('c:struct', 'timespec'),
# Standard C macros
('c:macro', 'LLONG_MAX'),
('c:macro', 'LLONG_MIN'),
@@ -251,12 +254,12 @@
('py:attr', '__wrapped__'),
]
-# gh-106948: Copy standard C types declared in the "c:type" domain to the
-# "c:identifier" domain, since "c:function" markup looks for types in the
-# "c:identifier" domain. Use list() to not iterate on items which are being
-# added
+# gh-106948: Copy standard C types declared in the "c:type" domain and C
+# structures declared in the "c:struct" domain to the "c:identifier" domain,
+# since "c:function" markup looks for types in the "c:identifier" domain. Use
+# list() to not iterate on items which are being added
for role, name in list(nitpick_ignore):
- if role == 'c:type':
+ if role in ('c:type', 'c:struct'):
nitpick_ignore.append(('c:identifier', name))
del role, name
diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst
index e22257853d83337..fdfbff341e7dc7d 100644
--- a/Doc/whatsnew/3.13.rst
+++ b/Doc/whatsnew/3.13.rst
@@ -1281,6 +1281,16 @@ New Features
* Add :c:func:`Py_HashPointer` function to hash a pointer.
(Contributed by Victor Stinner in :gh:`111545`.)
+* Add PyTime C API:
+
+ * :c:type:`PyTime_t` type.
+ * :c:var:`PyTime_MIN` and :c:var:`PyTime_MAX` constants.
+ * :c:func:`PyTime_AsSecondsDouble`, :c:func:`PyTime_Monotonic`,
+ :c:func:`PyTime_PerfCounter` and :c:func:`PyTime_Time`
+ functions.
+
+ (Contributed by Victor Stinner in :gh:`110850`.)
+
Porting to Python 3.13
----------------------
diff --git a/Include/Python.h b/Include/Python.h
index 196751c3201e620..01fc45137a17bbc 100644
--- a/Include/Python.h
+++ b/Include/Python.h
@@ -97,6 +97,7 @@
#include "weakrefobject.h"
#include "structseq.h"
#include "cpython/picklebufobject.h"
+#include "cpython/pytime.h"
#include "codecs.h"
#include "pyerrors.h"
#include "pythread.h"
diff --git a/Include/cpython/pytime.h b/Include/cpython/pytime.h
new file mode 100644
index 000000000000000..4179c15a4e67229
--- /dev/null
+++ b/Include/cpython/pytime.h
@@ -0,0 +1,23 @@
+// PyTime_t C API: see Doc/c-api/time.rst for the documentation.
+
+#ifndef Py_LIMITED_API
+#ifndef Py_PYTIME_H
+#define Py_PYTIME_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int64_t PyTime_t;
+#define PyTime_MIN INT64_MIN
+#define PyTime_MAX INT64_MAX
+
+PyAPI_FUNC(double) PyTime_AsSecondsDouble(PyTime_t t);
+PyAPI_FUNC(PyTime_t) PyTime_Monotonic(void);
+PyAPI_FUNC(PyTime_t) PyTime_PerfCounter(void);
+PyAPI_FUNC(PyTime_t) PyTime_Time(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* Py_PYTIME_H */
+#endif /* Py_LIMITED_API */
diff --git a/Include/internal/pycore_time.h b/Include/internal/pycore_time.h
index dabbd7b41556cdf..b2195a5b851d36d 100644
--- a/Include/internal/pycore_time.h
+++ b/Include/internal/pycore_time.h
@@ -1,34 +1,34 @@
-// The _PyTime_t API is written to use timestamp and timeout values stored in
-// various formats and to read clocks.
+// Internal PyTime_t C API: see Doc/c-api/time.rst for the documentation.
//
-// The _PyTime_t type is an integer to support directly common arithmetic
-// operations like t1 + t2.
+// The PyTime_t type is an integer to support directly common arithmetic
+// operations such as t1 + t2.
//
-// The _PyTime_t API supports a resolution of 1 nanosecond. The _PyTime_t type
-// is signed to support negative timestamps. The supported range is around
-// [-292.3 years; +292.3 years]. Using the Unix epoch (January 1st, 1970), the
-// supported date range is around [1677-09-21; 2262-04-11].
+// Time formats:
//
-// Formats:
-//
-// * seconds
-// * seconds as a floating pointer number (C double)
-// * milliseconds (10^-3 seconds)
-// * microseconds (10^-6 seconds)
-// * 100 nanoseconds (10^-7 seconds)
-// * nanoseconds (10^-9 seconds)
-// * timeval structure, 1 microsecond resolution (10^-6 seconds)
-// * timespec structure, 1 nanosecond resolution (10^-9 seconds)
+// * Seconds.
+// * Seconds as a floating point number (C double).
+// * Milliseconds (10^-3 seconds).
+// * Microseconds (10^-6 seconds).
+// * 100 nanoseconds (10^-7 seconds), used on Windows.
+// * Nanoseconds (10^-9 seconds).
+// * timeval structure, 1 microsecond (10^-6 seconds).
+// * timespec structure, 1 nanosecond (10^-9 seconds).
//
// Integer overflows are detected and raise OverflowError. Conversion to a
-// resolution worse than 1 nanosecond is rounded correctly with the requested
-// rounding mode. There are 4 rounding modes: floor (towards -inf), ceiling
-// (towards +inf), half even and up (away from zero).
+// resolution larger than 1 nanosecond is rounded correctly with the requested
+// rounding mode. Available rounding modes:
+//
+// * Round towards minus infinity (-inf). For example, used to read a clock.
+// * Round towards infinity (+inf). For example, used for timeout to wait "at
+// least" N seconds.
+// * Round to nearest with ties going to nearest even integer. For example, used
+// to round from a Python float.
+// * Round away from zero. For example, used for timeout.
//
-// Some functions clamp the result in the range [_PyTime_MIN; _PyTime_MAX], so
-// the caller doesn't have to handle errors and doesn't need to hold the GIL.
-// For example, _PyTime_Add(t1, t2) computes t1+t2 and clamp the result on
-// overflow.
+// Some functions clamp the result in the range [PyTime_MIN; PyTime_MAX]. The
+// caller doesn't have to handle errors and so doesn't need to hold the GIL to
+// handle exceptions. For example, _PyTime_Add(t1, t2) computes t1+t2 and
+// clamps the result on overflow.
//
// Clocks:
//
@@ -36,10 +36,11 @@
// * Monotonic clock
// * Performance counter
//
-// Operations like (t * k / q) with integers are implemented in a way to reduce
-// the risk of integer overflow. Such operation is used to convert a clock
-// value expressed in ticks with a frequency to _PyTime_t, like
-// QueryPerformanceCounter() with QueryPerformanceFrequency().
+// Internally, operations like (t * k / q) with integers are implemented in a
+// way to reduce the risk of integer overflow. Such operation is used to convert a
+// clock value expressed in ticks with a frequency to PyTime_t, like
+// QueryPerformanceCounter() with QueryPerformanceFrequency() on Windows.
+
#ifndef Py_INTERNAL_TIME_H
#define Py_INTERNAL_TIME_H
@@ -56,14 +57,7 @@ extern "C" {
struct timeval;
#endif
-// _PyTime_t: Python timestamp with subsecond precision. It can be used to
-// store a duration, and so indirectly a date (related to another date, like
-// UNIX epoch).
-typedef int64_t _PyTime_t;
-// _PyTime_MIN nanoseconds is around -292.3 years
-#define _PyTime_MIN INT64_MIN
-// _PyTime_MAX nanoseconds is around +292.3 years
-#define _PyTime_MAX INT64_MAX
+typedef PyTime_t _PyTime_t;
#define _SIZEOF_PYTIME_T 8
typedef enum {
@@ -147,7 +141,7 @@ PyAPI_FUNC(_PyTime_t) _PyTime_FromSecondsDouble(double seconds, _PyTime_round_t
PyAPI_FUNC(_PyTime_t) _PyTime_FromNanoseconds(_PyTime_t ns);
// Create a timestamp from a number of microseconds.
-// Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
+// Clamp to [PyTime_MIN; PyTime_MAX] on overflow.
extern _PyTime_t _PyTime_FromMicrosecondsClamp(_PyTime_t us);
// Create a timestamp from nanoseconds (Python int).
@@ -169,10 +163,6 @@ PyAPI_FUNC(int) _PyTime_FromMillisecondsObject(_PyTime_t *t,
PyObject *obj,
_PyTime_round_t round);
-// Convert a timestamp to a number of seconds as a C double.
-// Export for '_socket' shared extension.
-PyAPI_FUNC(double) _PyTime_AsSecondsDouble(_PyTime_t t);
-
// Convert timestamp to a number of milliseconds (10^-3 seconds).
// Export for '_ssl' shared extension.
PyAPI_FUNC(_PyTime_t) _PyTime_AsMilliseconds(_PyTime_t t,
@@ -250,7 +240,7 @@ PyAPI_FUNC(void) _PyTime_AsTimespec_clamp(_PyTime_t t, struct timespec *ts);
#endif
-// Compute t1 + t2. Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
+// Compute t1 + t2. Clamp to [PyTime_MIN; PyTime_MAX] on overflow.
extern _PyTime_t _PyTime_Add(_PyTime_t t1, _PyTime_t t2);
// Structure used by time.get_clock_info()
@@ -261,16 +251,6 @@ typedef struct {
double resolution;
} _Py_clock_info_t;
-// Get the current time from the system clock.
-//
-// If the internal clock fails, silently ignore the error and return 0.
-// On integer overflow, silently ignore the overflow and clamp the clock to
-// [_PyTime_MIN; _PyTime_MAX].
-//
-// Use _PyTime_GetSystemClockWithInfo() to check for failure.
-// Export for '_random' shared extension.
-PyAPI_FUNC(_PyTime_t) _PyTime_GetSystemClock(void);
-
// Get the current time from the system clock.
// On success, set *t and *info (if not NULL), and return 0.
// On error, raise an exception and return -1.
@@ -278,19 +258,6 @@ extern int _PyTime_GetSystemClockWithInfo(
_PyTime_t *t,
_Py_clock_info_t *info);
-// Get the time of a monotonic clock, i.e. a clock that cannot go backwards.
-// The clock is not affected by system clock updates. The reference point of
-// the returned value is undefined, so that only the difference between the
-// results of consecutive calls is valid.
-//
-// If the internal clock fails, silently ignore the error and return 0.
-// On integer overflow, silently ignore the overflow and clamp the clock to
-// [_PyTime_MIN; _PyTime_MAX].
-//
-// Use _PyTime_GetMonotonicClockWithInfo() to check for failure.
-// Export for '_random' shared extension.
-PyAPI_FUNC(_PyTime_t) _PyTime_GetMonotonicClock(void);
-
// Get the time of a monotonic clock, i.e. a clock that cannot go backwards.
// The clock is not affected by system clock updates. The reference point of
// the returned value is undefined, so that only the difference between the
@@ -315,17 +282,6 @@ PyAPI_FUNC(int) _PyTime_localtime(time_t t, struct tm *tm);
// Export for '_datetime' shared extension.
PyAPI_FUNC(int) _PyTime_gmtime(time_t t, struct tm *tm);
-// Get the performance counter: clock with the highest available resolution to
-// measure a short duration.
-//
-// If the internal clock fails, silently ignore the error and return 0.
-// On integer overflow, silently ignore the overflow and clamp the clock to
-// [_PyTime_MIN; _PyTime_MAX].
-//
-// Use _PyTime_GetPerfCounterWithInfo() to check for failure.
-// Export for '_lsprof' shared extension.
-PyAPI_FUNC(_PyTime_t) _PyTime_GetPerfCounter(void);
-
// Get the performance counter: clock with the highest available resolution to
// measure a short duration.
//
@@ -336,14 +292,22 @@ extern int _PyTime_GetPerfCounterWithInfo(
_PyTime_t *t,
_Py_clock_info_t *info);
+// Alias for backward compatibility
+#define _PyTime_AsSecondsDouble PyTime_AsSecondsDouble
+#define _PyTime_GetMonotonicClock PyTime_Monotonic
+#define _PyTime_GetPerfCounter PyTime_PerfCounter
+#define _PyTime_GetSystemClock PyTime_Time
+
+
+// --- _PyDeadline -----------------------------------------------------------
// Create a deadline.
-// Pseudo code: _PyTime_GetMonotonicClock() + timeout.
+// Pseudo code: PyTime_Monotonic() + timeout.
// Export for '_ssl' shared extension.
PyAPI_FUNC(_PyTime_t) _PyDeadline_Init(_PyTime_t timeout);
// Get remaining time from a deadline.
-// Pseudo code: deadline - _PyTime_GetMonotonicClock().
+// Pseudo code: deadline - PyTime_Monotonic().
// Export for '_ssl' shared extension.
PyAPI_FUNC(_PyTime_t) _PyDeadline_Get(_PyTime_t deadline);
diff --git a/Lib/test/test_capi/test_time.py b/Lib/test/test_capi/test_time.py
new file mode 100644
index 000000000000000..10b7fbf2c372a3b
--- /dev/null
+++ b/Lib/test/test_capi/test_time.py
@@ -0,0 +1,71 @@
+import time
+import unittest
+from test.support import import_helper
+_testcapi = import_helper.import_module('_testcapi')
+
+
+PyTime_MIN = _testcapi.PyTime_MIN
+PyTime_MAX = _testcapi.PyTime_MAX
+SEC_TO_NS = 10 ** 9
+DAY_TO_SEC = (24 * 60 * 60)
+# Worst clock resolution: maximum delta between two clock reads.
+CLOCK_RES = 0.050
+
+
+class CAPITest(unittest.TestCase):
+ def test_min_max(self):
+ # PyTime_t is just int64_t
+ self.assertEqual(PyTime_MIN, -2**63)
+ self.assertEqual(PyTime_MAX, 2**63 - 1)
+
+ def check_clock(self, c_func, py_func):
+ t1 = c_func()
+ t2 = py_func()
+ self.assertAlmostEqual(t1, t2, delta=CLOCK_RES)
+
+ def test_assecondsdouble(self):
+ # Test PyTime_AsSecondsDouble()
+ def ns_to_sec(ns):
+ if abs(ns) % SEC_TO_NS == 0:
+ return float(ns // SEC_TO_NS)
+ else:
+ return float(ns) / SEC_TO_NS
+
+ seconds = (
+ 0,
+ 1,
+ DAY_TO_SEC,
+ 365 * DAY_TO_SEC,
+ )
+ values = {
+ PyTime_MIN,
+ PyTime_MIN + 1,
+ PyTime_MAX - 1,
+ PyTime_MAX,
+ }
+ for second in seconds:
+ ns = second * SEC_TO_NS
+ values.add(ns)
+ # test nanosecond before/after to test rounding
+ values.add(ns - 1)
+ values.add(ns + 1)
+ for ns in list(values):
+ if (-ns) > PyTime_MAX:
+ continue
+ values.add(-ns)
+ for ns in sorted(values):
+ with self.subTest(ns=ns):
+ self.assertEqual(_testcapi.PyTime_AsSecondsDouble(ns),
+ ns_to_sec(ns))
+
+ def test_monotonic(self):
+ # Test PyTime_Monotonic()
+ self.check_clock(_testcapi.PyTime_Monotonic, time.monotonic)
+
+ def test_perf_counter(self):
+ # Test PyTime_PerfCounter()
+ self.check_clock(_testcapi.PyTime_PerfCounter, time.perf_counter)
+
+ def test_time(self):
+ # Test PyTime_time()
+ self.check_clock(_testcapi.PyTime_Time, time.time)
diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py
index 3b5640abdb6b892..a0aeea515afbd69 100644
--- a/Lib/test/test_time.py
+++ b/Lib/test/test_time.py
@@ -43,8 +43,8 @@ class _PyTime(enum.IntEnum):
ROUND_UP = 3
# _PyTime_t is int64_t
-_PyTime_MIN = -2 ** 63
-_PyTime_MAX = 2 ** 63 - 1
+PyTime_MIN = -2 ** 63
+PyTime_MAX = 2 ** 63 - 1
# Rounding modes supported by PyTime
ROUNDING_MODES = (
@@ -934,7 +934,7 @@ def test_FromSecondsObject(self):
_PyTime_FromSecondsObject(float('nan'), time_rnd)
def test_AsSecondsDouble(self):
- from _testinternalcapi import _PyTime_AsSecondsDouble
+ from _testcapi import PyTime_AsSecondsDouble
def float_converter(ns):
if abs(ns) % SEC_TO_NS == 0:
@@ -942,15 +942,10 @@ def float_converter(ns):
else:
return float(ns) / SEC_TO_NS
- self.check_int_rounding(lambda ns, rnd: _PyTime_AsSecondsDouble(ns),
+ self.check_int_rounding(lambda ns, rnd: PyTime_AsSecondsDouble(ns),
float_converter,
NS_TO_SEC)
- # test nan
- for time_rnd, _ in ROUNDING_MODES:
- with self.assertRaises(TypeError):
- _PyTime_AsSecondsDouble(float('nan'))
-
def create_decimal_converter(self, denominator):
denom = decimal.Decimal(denominator)
@@ -1009,7 +1004,7 @@ def test_AsTimeval_clamp(self):
tv_sec_max = self.time_t_max
tv_sec_min = self.time_t_min
- for t in (_PyTime_MIN, _PyTime_MAX):
+ for t in (PyTime_MIN, PyTime_MAX):
ts = _PyTime_AsTimeval_clamp(t, _PyTime.ROUND_CEILING)
with decimal.localcontext() as context:
context.rounding = decimal.ROUND_CEILING
@@ -1028,7 +1023,7 @@ def test_AsTimeval_clamp(self):
def test_AsTimespec_clamp(self):
from _testinternalcapi import _PyTime_AsTimespec_clamp
- for t in (_PyTime_MIN, _PyTime_MAX):
+ for t in (PyTime_MIN, PyTime_MAX):
ts = _PyTime_AsTimespec_clamp(t)
tv_sec, tv_nsec = divmod(t, NS_TO_SEC)
if self.time_t_max < tv_sec:
diff --git a/Misc/NEWS.d/next/C API/2023-11-16-02-07-48.gh-issue-110850.DQGNfF.rst b/Misc/NEWS.d/next/C API/2023-11-16-02-07-48.gh-issue-110850.DQGNfF.rst
new file mode 100644
index 000000000000000..50c6e9c4275b622
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2023-11-16-02-07-48.gh-issue-110850.DQGNfF.rst
@@ -0,0 +1,9 @@
+Add PyTime C API:
+
+* :c:type:`PyTime_t` type.
+* :c:var:`PyTime_MIN` and :c:var:`PyTime_MAX` constants.
+* :c:func:`PyTime_AsSecondsDouble`, :c:func:`PyTime_Monotonic`,
+ :c:func:`PyTime_PerfCounter` and :c:func:`PyTime_Time`
+ functions.
+
+Patch by Victor Stinner.
diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in
index 8a65a9cffb1b9d8..e98775a48087653 100644
--- a/Modules/Setup.stdlib.in
+++ b/Modules/Setup.stdlib.in
@@ -162,7 +162,7 @@
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c
-@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/bytearray.c _testcapi/bytes.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/pyos.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/heaptype_relative.c _testcapi/gc.c _testcapi/sys.c _testcapi/hash.c
+@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/bytearray.c _testcapi/bytes.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/pyos.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/heaptype_relative.c _testcapi/gc.c _testcapi/sys.c _testcapi/hash.c _testcapi/time.c
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
diff --git a/Modules/_randommodule.c b/Modules/_randommodule.c
index 4403e1d132c0570..80169de9ca93d74 100644
--- a/Modules/_randommodule.c
+++ b/Modules/_randommodule.c
@@ -262,10 +262,10 @@ random_seed_urandom(RandomObject *self)
static void
random_seed_time_pid(RandomObject *self)
{
- _PyTime_t now;
+ PyTime_t now;
uint32_t key[5];
- now = _PyTime_GetSystemClock();
+ now = PyTime_Time();
key[0] = (uint32_t)(now & 0xffffffffU);
key[1] = (uint32_t)(now >> 32);
@@ -277,7 +277,7 @@ random_seed_time_pid(RandomObject *self)
key[2] = 0;
#endif
- now = _PyTime_GetMonotonicClock();
+ now = PyTime_Monotonic();
key[3] = (uint32_t)(now & 0xffffffffU);
key[4] = (uint32_t)(now >> 32);
diff --git a/Modules/_testcapi/parts.h b/Modules/_testcapi/parts.h
index 29817edd69b1348..e8cfb2423500d4b 100644
--- a/Modules/_testcapi/parts.h
+++ b/Modules/_testcapi/parts.h
@@ -59,6 +59,7 @@ int _PyTestCapi_Init_Immortal(PyObject *module);
int _PyTestCapi_Init_GC(PyObject *module);
int _PyTestCapi_Init_Sys(PyObject *module);
int _PyTestCapi_Init_Hash(PyObject *module);
+int _PyTestCapi_Init_Time(PyObject *module);
int _PyTestCapi_Init_VectorcallLimited(PyObject *module);
int _PyTestCapi_Init_HeaptypeRelative(PyObject *module);
diff --git a/Modules/_testcapi/time.c b/Modules/_testcapi/time.c
new file mode 100644
index 000000000000000..b0389402ae7aa49
--- /dev/null
+++ b/Modules/_testcapi/time.c
@@ -0,0 +1,94 @@
+#include "parts.h"
+
+
+static int
+pytime_from_nanoseconds(PyTime_t *tp, PyObject *obj)
+{
+ if (!PyLong_Check(obj)) {
+ PyErr_Format(PyExc_TypeError, "expect int, got %s",
+ Py_TYPE(obj)->tp_name);
+ return -1;
+ }
+
+ long long nsec = PyLong_AsLongLong(obj);
+ if (nsec == -1 && PyErr_Occurred()) {
+ return -1;
+ }
+
+ Py_BUILD_ASSERT(sizeof(long long) == sizeof(PyTime_t));
+ *tp = (PyTime_t)nsec;
+ return 0;
+}
+
+
+static PyObject *
+test_pytime_assecondsdouble(PyObject *Py_UNUSED(self), PyObject *args)
+{
+ PyObject *obj;
+ if (!PyArg_ParseTuple(args, "O", &obj)) {
+ return NULL;
+ }
+ PyTime_t ts;
+ if (pytime_from_nanoseconds(&ts, obj) < 0) {
+ return NULL;
+ }
+ double d = PyTime_AsSecondsDouble(ts);
+ return PyFloat_FromDouble(d);
+}
+
+
+static PyObject*
+pytime_as_float(PyTime_t t)
+{
+ return PyFloat_FromDouble(PyTime_AsSecondsDouble(t));
+}
+
+
+
+static PyObject*
+test_pytime_monotonic(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
+{
+ PyTime_t t = PyTime_Monotonic();
+ return pytime_as_float(t);
+}
+
+
+static PyObject*
+test_pytime_perf_counter(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
+{
+ PyTime_t t = PyTime_PerfCounter();
+ return pytime_as_float(t);
+}
+
+
+static PyObject*
+test_pytime_time(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
+{
+ PyTime_t t = PyTime_Time();
+ return pytime_as_float(t);
+}
+
+
+static PyMethodDef test_methods[] = {
+ {"PyTime_AsSecondsDouble", test_pytime_assecondsdouble, METH_VARARGS},
+ {"PyTime_Monotonic", test_pytime_monotonic, METH_NOARGS},
+ {"PyTime_PerfCounter", test_pytime_perf_counter, METH_NOARGS},
+ {"PyTime_Time", test_pytime_time, METH_NOARGS},
+ {NULL},
+};
+
+int
+_PyTestCapi_Init_Time(PyObject *m)
+{
+ if (PyModule_AddFunctions(m, test_methods) < 0) {
+ return -1;
+ }
+ Py_BUILD_ASSERT(sizeof(long long) == sizeof(PyTime_t));
+ if (PyModule_AddObject(m, "PyTime_MIN", PyLong_FromLongLong(PyTime_MIN)) < 0) {
+ return 1;
+ }
+ if (PyModule_AddObject(m, "PyTime_MAX", PyLong_FromLongLong(PyTime_MAX)) < 0) {
+ return 1;
+ }
+ return 0;
+}
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index b3ddfae58e6fc00..c0c71d664093ffc 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -4039,6 +4039,9 @@ PyInit__testcapi(void)
if (_PyTestCapi_Init_Hash(m) < 0) {
return NULL;
}
+ if (_PyTestCapi_Init_Time(m) < 0) {
+ return NULL;
+ }
PyState_AddModule(m, &_testcapimodule);
return m;
diff --git a/Modules/_testinternalcapi/pytime.c b/Modules/_testinternalcapi/pytime.c
index 2b5f9eb0ef28511..f0f758ea032df82 100644
--- a/Modules/_testinternalcapi/pytime.c
+++ b/Modules/_testinternalcapi/pytime.c
@@ -52,21 +52,6 @@ test_pytime_fromsecondsobject(PyObject *self, PyObject *args)
return _PyTime_AsNanosecondsObject(ts);
}
-static PyObject *
-test_pytime_assecondsdouble(PyObject *self, PyObject *args)
-{
- PyObject *obj;
- if (!PyArg_ParseTuple(args, "O", &obj)) {
- return NULL;
- }
- _PyTime_t ts;
- if (_PyTime_FromNanosecondsObject(&ts, obj) < 0) {
- return NULL;
- }
- double d = _PyTime_AsSecondsDouble(ts);
- return PyFloat_FromDouble(d);
-}
-
static PyObject *
test_PyTime_AsTimeval(PyObject *self, PyObject *args)
{
@@ -254,7 +239,6 @@ test_pytime_object_to_timespec(PyObject *self, PyObject *args)
static PyMethodDef TestMethods[] = {
{"_PyTime_AsMicroseconds", test_PyTime_AsMicroseconds, METH_VARARGS},
{"_PyTime_AsMilliseconds", test_PyTime_AsMilliseconds, METH_VARARGS},
- {"_PyTime_AsSecondsDouble", test_pytime_assecondsdouble, METH_VARARGS},
#ifdef HAVE_CLOCK_GETTIME
{"_PyTime_AsTimespec", test_PyTime_AsTimespec, METH_VARARGS},
{"_PyTime_AsTimespec_clamp", test_PyTime_AsTimespec_clamp, METH_VARARGS},
diff --git a/PCbuild/_testcapi.vcxproj b/PCbuild/_testcapi.vcxproj
index 1c15541d3ec735f..959c35e2660c919 100644
--- a/PCbuild/_testcapi.vcxproj
+++ b/PCbuild/_testcapi.vcxproj
@@ -125,6 +125,7 @@
+
diff --git a/PCbuild/_testcapi.vcxproj.filters b/PCbuild/_testcapi.vcxproj.filters
index 6059959bb9a040f..651eb1d6ba0b7f3 100644
--- a/PCbuild/_testcapi.vcxproj.filters
+++ b/PCbuild/_testcapi.vcxproj.filters
@@ -105,6 +105,9 @@
Source Files
+
+ Source Files
+
Source Files
diff --git a/Python/pytime.c b/Python/pytime.c
index 77cb95f8feb179f..e0bcecf0aa25d8d 100644
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -50,7 +50,7 @@
# error "time_t is not a two's complement integer type"
#endif
-#if _PyTime_MIN + _PyTime_MAX != -1
+#if PyTime_MIN + PyTime_MAX != -1
# error "_PyTime_t is not a two's complement integer type"
#endif
@@ -124,16 +124,16 @@ pytime_as_nanoseconds(_PyTime_t t)
}
-// Compute t1 + t2. Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
+// Compute t1 + t2. Clamp to [PyTime_MIN; PyTime_MAX] on overflow.
static inline int
pytime_add(_PyTime_t *t1, _PyTime_t t2)
{
- if (t2 > 0 && *t1 > _PyTime_MAX - t2) {
- *t1 = _PyTime_MAX;
+ if (t2 > 0 && *t1 > PyTime_MAX - t2) {
+ *t1 = PyTime_MAX;
return -1;
}
- else if (t2 < 0 && *t1 < _PyTime_MIN - t2) {
- *t1 = _PyTime_MIN;
+ else if (t2 < 0 && *t1 < PyTime_MIN - t2) {
+ *t1 = PyTime_MIN;
return -1;
}
else {
@@ -156,7 +156,7 @@ pytime_mul_check_overflow(_PyTime_t a, _PyTime_t b)
{
if (b != 0) {
assert(b > 0);
- return ((a < _PyTime_MIN / b) || (_PyTime_MAX / b < a));
+ return ((a < PyTime_MIN / b) || (PyTime_MAX / b < a));
}
else {
return 0;
@@ -164,13 +164,13 @@ pytime_mul_check_overflow(_PyTime_t a, _PyTime_t b)
}
-// Compute t * k. Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
+// Compute t * k. Clamp to [PyTime_MIN; PyTime_MAX] on overflow.
static inline int
pytime_mul(_PyTime_t *t, _PyTime_t k)
{
assert(k >= 0);
if (pytime_mul_check_overflow(*t, k)) {
- *t = (*t >= 0) ? _PyTime_MAX : _PyTime_MIN;
+ *t = (*t >= 0) ? PyTime_MAX : PyTime_MIN;
return -1;
}
else {
@@ -180,7 +180,7 @@ pytime_mul(_PyTime_t *t, _PyTime_t k)
}
-// Compute t * k. Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow.
+// Compute t * k. Clamp to [PyTime_MIN; PyTime_MAX] on overflow.
static inline _PyTime_t
_PyTime_Mul(_PyTime_t t, _PyTime_t k)
{
@@ -459,12 +459,12 @@ _PyTime_FromSeconds(int seconds)
/* ensure that integer overflow cannot happen, int type should have 32
bits, whereas _PyTime_t type has at least 64 bits (SEC_TO_NS takes 30
bits). */
- static_assert(INT_MAX <= _PyTime_MAX / SEC_TO_NS, "_PyTime_t overflow");
- static_assert(INT_MIN >= _PyTime_MIN / SEC_TO_NS, "_PyTime_t underflow");
+ static_assert(INT_MAX <= PyTime_MAX / SEC_TO_NS, "_PyTime_t overflow");
+ static_assert(INT_MIN >= PyTime_MIN / SEC_TO_NS, "_PyTime_t underflow");
_PyTime_t t = (_PyTime_t)seconds;
- assert((t >= 0 && t <= _PyTime_MAX / SEC_TO_NS)
- || (t < 0 && t >= _PyTime_MIN / SEC_TO_NS));
+ assert((t >= 0 && t <= PyTime_MAX / SEC_TO_NS)
+ || (t < 0 && t >= PyTime_MIN / SEC_TO_NS));
t *= SEC_TO_NS;
return pytime_from_nanoseconds(t);
}
@@ -587,7 +587,7 @@ pytime_from_double(_PyTime_t *tp, double value, _PyTime_round_t round,
d = pytime_round(d, round);
/* See comments in pytime_double_to_denominator */
- if (!((double)_PyTime_MIN <= d && d < -(double)_PyTime_MIN)) {
+ if (!((double)PyTime_MIN <= d && d < -(double)PyTime_MIN)) {
pytime_time_t_overflow();
return -1;
}
@@ -649,12 +649,12 @@ _PyTime_FromMillisecondsObject(_PyTime_t *tp, PyObject *obj, _PyTime_round_t rou
double
-_PyTime_AsSecondsDouble(_PyTime_t t)
+PyTime_AsSecondsDouble(PyTime_t t)
{
/* volatile avoids optimization changing how numbers are rounded */
volatile double d;
- _PyTime_t ns = pytime_as_nanoseconds(t);
+ PyTime_t ns = pytime_as_nanoseconds(t);
if (ns % SEC_TO_NS == 0) {
/* Divide using integers to avoid rounding issues on the integer part.
1e-9 cannot be stored exactly in IEEE 64-bit. */
@@ -695,7 +695,7 @@ pytime_divide_round_up(const _PyTime_t t, const _PyTime_t k)
assert(k > 1);
if (t >= 0) {
// Don't use (t + k - 1) / k to avoid integer overflow
- // if t is equal to _PyTime_MAX
+ // if t is equal to PyTime_MAX
_PyTime_t q = t / k;
if (t % k) {
q += 1;
@@ -704,7 +704,7 @@ pytime_divide_round_up(const _PyTime_t t, const _PyTime_t k)
}
else {
// Don't use (t - (k - 1)) / k to avoid integer overflow
- // if t is equals to _PyTime_MIN.
+ // if t is equals to PyTime_MIN.
_PyTime_t q = t / k;
if (t % k) {
q -= 1;
@@ -759,7 +759,7 @@ pytime_divide(const _PyTime_t t, const _PyTime_t k,
// Compute (t / k, t % k) in (pq, pr).
// Make sure that 0 <= pr < k.
// Return 0 on success.
-// Return -1 on underflow and store (_PyTime_MIN, 0) in (pq, pr).
+// Return -1 on underflow and store (PyTime_MIN, 0) in (pq, pr).
static int
pytime_divmod(const _PyTime_t t, const _PyTime_t k,
_PyTime_t *pq, _PyTime_t *pr)
@@ -768,8 +768,8 @@ pytime_divmod(const _PyTime_t t, const _PyTime_t k,
_PyTime_t q = t / k;
_PyTime_t r = t % k;
if (r < 0) {
- if (q == _PyTime_MIN) {
- *pq = _PyTime_MIN;
+ if (q == PyTime_MIN) {
+ *pq = PyTime_MIN;
*pr = 0;
return -1;
}
@@ -1037,10 +1037,10 @@ py_get_system_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
}
-_PyTime_t
-_PyTime_GetSystemClock(void)
+PyTime_t
+PyTime_Time(void)
{
- _PyTime_t t;
+ PyTime_t t;
if (py_get_system_clock(&t, NULL, 0) < 0) {
// If clock_gettime(CLOCK_REALTIME) or gettimeofday() fails:
// silently ignore the failure and return 0.
@@ -1102,13 +1102,13 @@ py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
static_assert(sizeof(ticks) <= sizeof(_PyTime_t),
"ULONGLONG is larger than _PyTime_t");
_PyTime_t t;
- if (ticks <= (ULONGLONG)_PyTime_MAX) {
+ if (ticks <= (ULONGLONG)PyTime_MAX) {
t = (_PyTime_t)ticks;
}
else {
// GetTickCount64() maximum is larger than _PyTime_t maximum:
// ULONGLONG is unsigned, whereas _PyTime_t is signed.
- t = _PyTime_MAX;
+ t = PyTime_MAX;
}
int res = pytime_mul(&t, MS_TO_NS);
@@ -1151,7 +1151,7 @@ py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
uint64_t uticks = mach_absolute_time();
// unsigned => signed
- assert(uticks <= (uint64_t)_PyTime_MAX);
+ assert(uticks <= (uint64_t)PyTime_MAX);
_PyTime_t ticks = (_PyTime_t)uticks;
_PyTime_t ns = _PyTimeFraction_Mul(ticks, &base);
@@ -1216,10 +1216,10 @@ py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
}
-_PyTime_t
-_PyTime_GetMonotonicClock(void)
+PyTime_t
+PyTime_Monotonic(void)
{
- _PyTime_t t;
+ PyTime_t t;
if (py_get_monotonic_clock(&t, NULL, 0) < 0) {
// If mach_timebase_info(), clock_gettime() or gethrtime() fails:
// silently ignore the failure and return 0.
@@ -1316,10 +1316,10 @@ _PyTime_GetPerfCounterWithInfo(_PyTime_t *t, _Py_clock_info_t *info)
}
-_PyTime_t
-_PyTime_GetPerfCounter(void)
+PyTime_t
+PyTime_PerfCounter(void)
{
- _PyTime_t t;
+ PyTime_t t;
int res;
#ifdef MS_WINDOWS
res = py_get_win_perf_counter(&t, NULL, 0);
@@ -1405,7 +1405,7 @@ _PyTime_gmtime(time_t t, struct tm *tm)
_PyTime_t
_PyDeadline_Init(_PyTime_t timeout)
{
- _PyTime_t now = _PyTime_GetMonotonicClock();
+ _PyTime_t now = PyTime_Monotonic();
return _PyTime_Add(now, timeout);
}
@@ -1413,6 +1413,6 @@ _PyDeadline_Init(_PyTime_t timeout)
_PyTime_t
_PyDeadline_Get(_PyTime_t deadline)
{
- _PyTime_t now = _PyTime_GetMonotonicClock();
+ _PyTime_t now = PyTime_Monotonic();
return deadline - now;
}