Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions Doc/c-api/time.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
.. highlight:: c

PyTime C API
============

.. versionadded:: 3.13

The clock C API provides access to system clocks and time conversion functions.
It is similar to the Python :mod:`time` module.

For C API related to the :mod:`datetime` module, see :ref:`datetimeobjects`.


Types
-----

.. c:type:: PyTime_t

A timestamp or duration.

The reference point for timestamps depends on the clock used. For example,
:c:func:`PyTime_Time` returns timestamps relative to the UNIX epoch.

``PyTime_t`` is a signed integral type. It is possible to add and
subtract such values directly.
Keep in mind that C overflow rules do apply.

``PyTime_t`` can represent time with nanosecond or better resolution.
The unit used is an internal implementation detail; use
:c:func:`PyTime_AsNanoseconds` or :c:func:`PyTime_AsSecondsDouble`
to convert to a given unit.

The supported range is at least around [-292.3 years; +292.3 years].
Using the Unix epoch (January 1st, 1970) as reference, the supported date
range is at least around [1677-09-21; 2262-04-11].
The exact limits are exposed as constants.

.. c:var:: PyTime_t PyTime_MIN
Comment thread
encukou marked this conversation as resolved.
Outdated

Minimum value of :c:type:`PyTime_t`.

.. c:var:: PyTime_t PyTime_MAX

Maximum value of :c:type:`PyTime_t`.


Clock Functions
---------------

The following functions take a pointer to a :c:expr:`PyTime_t` that they
set to the value of a particular clock.
Details of each clock are given in the documentation of the corresponding
Python function.

The functions return 0 on success, or -1 (with an exception set) on failure.
Comment thread
encukou marked this conversation as resolved.
Outdated

On integer overflow, they set the :c:data:`PyExc_OverflowError` exception and
set :c:expr:`*result` to the value clamped to the ``[PyTime_MIN; PyTime_MAX]``
range.
(On current systems, integer overflows are likely caused by misconfigured
system time.)

.. c:function:: int PyTime_Monotonic(PyTime_t *result)

Read the monotonic clock.
See :func:`time.monotonic` for important details on this clock.

.. c:function:: int PyTime_PerfCounter(PyTime_t *result)

Read the performance counter.
See :func:`time.perf_counter` for important details on this clock.

.. c:function:: int PyTime_Time(PyTime_t *result)

Read the “wall clock” time.
See :func:`time.time` for details important on this clock.


Conversion 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, but note that :c:expr:`double` has limited
accuracy for large values.

.. c:function:: int PyTime_AsNanoseconds(PyTime_t t, int64_t *result)
Comment thread
encukou marked this conversation as resolved.
Outdated

Convert a timestamp to a number of nanoseconds as a 64-bit signed integer.

Returns 0 on success, or -1 (with an exception set) on failure.
Comment thread
encukou marked this conversation as resolved.
Outdated
Comment thread
encukou marked this conversation as resolved.
Outdated
1 change: 1 addition & 0 deletions Doc/c-api/utilities.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
13 changes: 8 additions & 5 deletions Doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,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'),
Expand Down Expand Up @@ -269,12 +272,12 @@
('py:meth', 'index'), # list.index, tuple.index, etc.
]

# 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

Expand Down
10 changes: 10 additions & 0 deletions Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1501,6 +1501,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_AsNanoseconds`,
:c:func:`PyTime_Monotonic`, :c:func:`PyTime_PerfCounter`, and
:c:func:`PyTime_Time` functions.

(Contributed by Victor Stinner and Petr Viktorin in :gh:`110850`.)


Porting to Python 3.13
----------------------
Expand Down
1 change: 1 addition & 0 deletions Include/Python.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
36 changes: 36 additions & 0 deletions Include/cpython/pytime.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// 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 PY_INT64_T PyTime_t;
Comment thread
encukou marked this conversation as resolved.
Outdated
#define PyTime_MIN INT64_MIN
#define PyTime_MAX INT64_MAX

PyAPI_FUNC(double) PyTime_AsSecondsDouble(PyTime_t t);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: i suggest to first declare functions "creating" PyTime_t values, and then in second just (just add an empty line) functions to "convert" PyTime_t values.

PyAPI_FUNC(int) PyTime_AsNanoseconds(PyTime_t t, PY_INT64_T *result);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
PyAPI_FUNC(int) PyTime_AsNanoseconds(PyTime_t t, PY_INT64_T *result);
PyAPI_FUNC(int) PyTime_AsNanoseconds(PyTime_t t, int64_t *result);

PyAPI_FUNC(int) PyTime_Monotonic(PyTime_t *result);
PyAPI_FUNC(int) PyTime_PerfCounter(PyTime_t *result);
PyAPI_FUNC(int) PyTime_Time(PyTime_t *result);

static inline int
_PyTime_AsNanoseconds_impl(PyTime_t t, PY_INT64_T *result)
{
// Implementation detail: PyTime_t is already a int64_t in nanoseconds.
// This may change. We might e.g. extend PyTime_t to 128 bits, then
// this function can start raising OverflowError.
*result = t;
return 0;
}

#define PyTime_AsNanoseconds _PyTime_AsNanoseconds_impl

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that it's worth it to add such static inline function which is an optimization.


#ifdef __cplusplus
}
#endif
#endif /* Py_PYTIME_H */
#endif /* Py_LIMITED_API */
96 changes: 47 additions & 49 deletions Include/internal/pycore_time.h
Original file line number Diff line number Diff line change
@@ -1,45 +1,46 @@
// 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:
//
// * System clock
// * 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
Expand All @@ -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 {
Expand Down Expand Up @@ -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).
Expand All @@ -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,
Expand All @@ -183,9 +173,6 @@ PyAPI_FUNC(_PyTime_t) _PyTime_AsMilliseconds(_PyTime_t t,
PyAPI_FUNC(_PyTime_t) _PyTime_AsMicroseconds(_PyTime_t t,
_PyTime_round_t round);

// Convert timestamp to a number of nanoseconds (10^-9 seconds).
extern _PyTime_t _PyTime_AsNanoseconds(_PyTime_t t);

#ifdef MS_WINDOWS
// Convert timestamp to a number of 100 nanoseconds (10^-7 seconds).
extern _PyTime_t _PyTime_As100Nanoseconds(_PyTime_t t,
Expand Down Expand Up @@ -250,7 +237,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()
Expand All @@ -267,7 +254,8 @@ typedef struct {
// On integer overflow, silently ignore the overflow and clamp the clock to
// [_PyTime_MIN; _PyTime_MAX].
//
// Use _PyTime_GetSystemClockWithInfo() to check for failure.
// Use _PyTime_GetSystemClockWithInfo or the public PyTime_Time() to check
// for failure.
// Export for '_random' shared extension.
PyAPI_FUNC(_PyTime_t) _PyTime_GetSystemClock(void);

Expand All @@ -287,7 +275,8 @@ extern int _PyTime_GetSystemClockWithInfo(
// On integer overflow, silently ignore the overflow and clamp the clock to
// [_PyTime_MIN; _PyTime_MAX].
//
// Use _PyTime_GetMonotonicClockWithInfo() to check for failure.
// Use _PyTime_GetMonotonicClockWithInfo or the public PyTime_Monotonic()
// to check for failure.
// Export for '_random' shared extension.
PyAPI_FUNC(_PyTime_t) _PyTime_GetMonotonicClock(void);

Expand Down Expand Up @@ -322,10 +311,12 @@ PyAPI_FUNC(int) _PyTime_gmtime(time_t t, struct tm *tm);
// On integer overflow, silently ignore the overflow and clamp the clock to
// [_PyTime_MIN; _PyTime_MAX].
//
// Use _PyTime_GetPerfCounterWithInfo() to check for failure.
// Use _PyTime_GetPerfCounterWithInfo() or the public PyTime_PerfCounter
// 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.
//
Expand All @@ -336,6 +327,13 @@ extern int _PyTime_GetPerfCounterWithInfo(
_PyTime_t *t,
_Py_clock_info_t *info);

// Alias for backward compatibility
#define _PyTime_MIN PyTime_MIN
#define _PyTime_MAX PyTime_MAX
#define _PyTime_AsSecondsDouble PyTime_AsSecondsDouble


// --- _PyDeadline -----------------------------------------------------------

// Create a deadline.
// Pseudo code: _PyTime_GetMonotonicClock() + timeout.
Expand Down
Loading