This commit is contained in:
Victor Stinner 2012-02-08 14:31:50 +01:00
parent 6f91ce74a0
commit ccd5715a14
9 changed files with 814 additions and 174 deletions

View File

@ -808,13 +808,16 @@ as internal buffering of data.
Availability: Unix.
.. function:: fstat(fd)
.. function:: fstat(fd, timestamp=None)
Return status for file descriptor *fd*, like :func:`~os.stat`.
Availability: Unix, Windows.
.. function:: fstatat(dirfd, path, flags=0)
.. versionchanged:: 3.3
Added the *timestamp* argument.
.. function:: fstatat(dirfd, path, flags=0, timestamp="float")
Like :func:`stat` but if *path* is relative, it is taken as relative to *dirfd*.
*flags* is optional and may be 0 or :data:`AT_SYMLINK_NOFOLLOW`.
@ -1696,7 +1699,7 @@ Files and Directories
.. versionadded:: 3.3
.. function:: lstat(path)
.. function:: lstat(path, timestamp=None)
Perform the equivalent of an :c:func:`lstat` system call on the given path.
Similar to :func:`~os.stat`, but does not follow symbolic links. On
@ -1706,6 +1709,9 @@ Files and Directories
.. versionchanged:: 3.2
Added support for Windows 6.0 (Vista) symbolic links.
.. versionchanged:: 3.3
The *timestamp* argument was added.
.. function:: lutimes(path[, times])
@ -1969,7 +1975,7 @@ Files and Directories
.. versionadded:: 3.3
.. function:: stat(path)
.. function:: stat(path, timestamp=None)
Perform the equivalent of a :c:func:`stat` system call on the given path.
(This function follows symlinks; to stat a symlink use :func:`lstat`.)
@ -1989,6 +1995,11 @@ Files and Directories
* :attr:`st_ctime` - platform dependent; time of most recent metadata change on
Unix, or the time of creation on Windows)
:attr:`st_atime`, :attr:`st_mtime` and :attr:`st_ctime` are :class:`float`
by default, or :class:`int` if :func:`os.stat_float_times` is ``False``. Set
the *timestamp* argument to get another :ref:`timestamp type
<timestamp-types>`.
On some Unix systems (such as Linux), the following attributes may also be
available:
@ -2044,6 +2055,9 @@ Files and Directories
Availability: Unix, Windows.
.. versionchanged:: 3.3
Added the *timestamp* argument.
.. function:: stat_float_times([newvalue])
@ -2069,6 +2083,9 @@ Files and Directories
are processed, this application should turn the feature off until the library
has been corrected.
.. deprecated:: 3.3
Use *timestamp* argument of stat functions instead.
.. function:: statvfs(path)
@ -2859,27 +2876,39 @@ written in Python, such as a mail server's external command delivery program.
with :const:`P_NOWAIT` return suitable process handles.
.. function:: wait3([options])
.. function:: wait3(options[, timestamp=float])
Similar to :func:`waitpid`, except no process id argument is given and a
3-element tuple containing the child's process id, exit status indication, and
resource usage information is returned. Refer to :mod:`resource`.\
:func:`getrusage` for details on resource usage information. The option
argument is the same as that provided to :func:`waitpid` and :func:`wait4`.
:attr:`ru_utime` and :attr:`ru_stime` attributes of the resource usage are
:class:`float` by default, set the *timestamp* argument to get another
:ref:`timestamp type <timestamp-types>`.
Availability: Unix.
.. versionchanged:: 3.3
Added the *timestamp* argument.
.. function:: wait4(pid, options)
.. function:: wait4(pid, options[, timestamp=float])
Similar to :func:`waitpid`, except a 3-element tuple, containing the child's
process id, exit status indication, and resource usage information is returned.
Refer to :mod:`resource`.\ :func:`getrusage` for details on resource usage
information. The arguments to :func:`wait4` are the same as those provided to
:func:`waitpid`.
:attr:`ru_utime` and :attr:`ru_stime` attributes of the resource usage are
:class:`float` by default, set the *timestamp* argument to get another
:ref:`timestamp type <timestamp-types>`.
Availability: Unix.
.. versionchanged:: 3.3
Added the *timestamp* argument.
.. data:: WNOHANG

View File

@ -95,6 +95,14 @@ An explanation of some terminology and conventions is in order.
| local time | | |
+-------------------------+-------------------------+-------------------------+
.. _timestamp-types:
* Python supports the following timestamp types:
* :class:`int`
* :class:`float`
* :class:`decimal.Decimal`
The module defines the following functions and data items:
@ -119,7 +127,7 @@ The module defines the following functions and data items:
trailing newline.
.. function:: clock()
.. function:: clock(timestamp=float)
.. index::
single: CPU time
@ -136,16 +144,27 @@ The module defines the following functions and data items:
:c:func:`QueryPerformanceCounter`. The resolution is typically better than one
microsecond.
Return as a floating point number by default, set the *timestamp* argument
to get another :ref:`timestamp type <timestamp-types>`.
.. function:: clock_getres(clk_id)
.. versionchanged:: 3.3
Added the *timestamp* argument.
.. function:: clock_getres(clk_id, timestamp=float)
Return the resolution (precision) of the specified clock *clk_id*.
Return a floating point number by default, set the *timestamp* argument to
get another :ref:`timestamp type <timestamp-types>`.
.. versionadded:: 3.3
.. function:: clock_gettime(clk_id)
.. function:: clock_gettime(clk_id, timestamp=float)
Return the time of the specified clock *clk_id*.
Return a floating point number by default, set the *timestamp* argument to
get another :ref:`timestamp type <timestamp-types>`.
.. versionadded:: 3.3
@ -214,19 +233,22 @@ The module defines the following functions and data items:
flag is set to ``1`` when DST applies to the given time.
.. function:: mktime(t)
.. function:: mktime(t, timestamp=float)
This is the inverse function of :func:`localtime`. Its argument is the
:class:`struct_time` or full 9-tuple (since the dst flag is needed; use ``-1``
as the dst flag if it is unknown) which expresses the time in *local* time, not
UTC. It returns a floating point number, for compatibility with :func:`time`.
It returns a floating point number by default, for compatibility with
:func:`time`, set the *timestamp* argument to get another :ref:`timestamp
type <timestamp-types>`.
If the input value cannot be represented as a valid time, either
:exc:`OverflowError` or :exc:`ValueError` will be raised (which depends on
whether the invalid value is caught by Python or the underlying C libraries).
The earliest date for which it can generate a time is platform-dependent.
.. function:: monotonic()
.. function:: monotonic(timestamp=float)
Monotonic clock. The reference point of the returned value is undefined so
only the difference of consecutive calls is valid.
@ -440,15 +462,20 @@ The module defines the following functions and data items:
:exc:`TypeError` is raised.
.. function:: time()
.. function:: time(timestamp=float)
Return the time as a floating point number expressed in seconds since the epoch,
in UTC. Note that even though the time is always returned as a floating point
Return the time expressed in seconds since the epoch in UTC. Return a
floating point number by default, set the *timestamp* argument to get
another :ref:`timestamp type <timestamp-types>`.
Note that even though the time is always returned as a floating point
number, not all systems provide time with a better precision than 1 second.
While this function normally returns non-decreasing values, it can return a
lower value than a previous call if the system clock has been set back between
the two calls.
.. versionchanged:: 3.3
Added the *timestamp* argument.
.. data:: timezone
@ -546,13 +573,16 @@ The module defines the following functions and data items:
('EET', 'EEST')
.. function:: wallclock()
.. function:: wallclock(timestamp=float)
.. index::
single: Wallclock
single: benchmarking
Return the current time in fractions of a second to the system's best ability.
Return a floating point number by default, set the *timestamp* argument to
get another :ref:`timestamp type <timestamp-types>`.
Use this when the most accurate representation of wall-clock is required, i.e.
when "processor time" is inappropriate. The reference point of the returned
value is undefined so only the difference of consecutive calls is valid.

View File

@ -270,6 +270,42 @@ new, more precise information::
'<function C.D.meth at 0x7f46b9fe31e0>'
PEP 410: Use decimal.Decimal type for timestamps
================================================
:pep:`410` - Use decimal.Decimal type for timestamps
PEP written and implemented by Victor Stinner.
The following functions have a new optional *timestamp* argument to get a
timestamp as a :class:`decimal.Decimal` instead of :class:`int` or
:class:`float`:
* :mod:`time` module: :func:`~time.clock`, :func:`~time.clock_gettime`,
:func:`~time.clock_getres`, :func:`~time.monotonic`, :func:`~time.time` and
:func:`~time.wallclock`
* :mod:`os` module: :func:`~os.fstat`, :func:`~os.fstatat`, :func:`~os.lstat`
and :func:`~os.stat` (``st_atime``, ``st_ctime`` and ``st_mtime`` fields of
the stat structure)
:class:`decimal.Decimal` supports a resolution of a nanosecond (10^-9)
resolution, whereas :class:`float` has only a resolution of a microsecond
(10^-6) in common cases. See the list of available :ref:`timestamp types
<timestamp-types>`.
Example::
>>> import decimal, time
>>> time.time()
1328006975.681211
>>> time.time(timestamp=int)
1328006979
>>> time.time(timestamp=decimal.Decimal)
Decimal('1328006983.761119')
:func:`os.stat_float_times` has been deprecated, use *timestamp* argument of
`os.stat` instead.
Other Language Changes
======================

View File

@ -2,7 +2,8 @@
#ifndef Py_PYTIME_H
#define Py_PYTIME_H
#include "pyconfig.h" /* include for defines */
#include "pyport.h"
#include "object.h"
/**************************************************************************
Symbols and macros to supply platform-independent interfaces to time related
@ -37,6 +38,31 @@ do { \
((tv_end.tv_sec - tv_start.tv_sec) + \
(tv_end.tv_usec - tv_start.tv_usec) * 0.000001)
#if defined(HAVE_LONG_LONG)
typedef unsigned PY_LONG_LONG _PyTime_fraction_t;
#else
typedef size_t _PyTime_fraction_t;
#endif
typedef struct
{
/* timestamp = seconds + numerator / denominator */
time_t seconds;
_PyTime_fraction_t numerator;
/* denominator cannot be zero */
_PyTime_fraction_t denominator;
/* the timestamp resolution is 1/divisor */
} _PyTime_t;
/* Similar to POSIX gettimeofday. If system gettimeofday
fails or is not available, fall back to lower resolution clocks. */
PyAPI_FUNC(void) _PyTime_get(_PyTime_t *tp);
/* Convert a timestamp structure to the specified timestamp type.
Raise a ValueError if the timestamp type is unknown. */
PyAPI_FUNC(PyObject*) _PyTime_Convert(_PyTime_t *ts, PyObject *timestamp);
/* Dummy to force linking. */
PyAPI_FUNC(void) _PyTime_Init(void);

View File

@ -2,6 +2,7 @@
# does add tests for a few functions which have been determined to be more
# portable than they had been thought to be.
import decimal
import os
import errno
import unittest
@ -238,6 +239,36 @@ class StatAttributeTests(unittest.TestCase):
warnings.simplefilter("ignore", DeprecationWarning)
self.check_stat_attributes(fname)
def test_stat_timestamp(self):
# test deprecation
with warnings.catch_warnings():
warnings.simplefilter("error", DeprecationWarning)
self.assertRaises(DeprecationWarning, os.stat_float_times, False)
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
old_value = os.stat_float_times()
try:
# test invalid timestamp types
self.assertRaises(ValueError, os.stat, self.fname,
timestamp="abc")
self.assertRaises(ValueError, os.stat, self.fname,
timestamp=decimal.Context)
for float_times in (False, True):
os.stat_float_times(float_times)
t = os.stat(self.fname).st_mtime
if float_times:
self.assertIsInstance(t, float)
else:
self.assertIsInstance(t, int)
for type in (int, float, decimal.Decimal):
t = os.stat(self.fname, timestamp=type).st_mtime
self.assertIsInstance(t, type)
finally:
os.stat_float_times(old_value)
def test_statvfs_attributes(self):
if not hasattr(os, "statvfs"):
return

View File

@ -1,10 +1,10 @@
import locale
import platform
import sys
import sysconfig
from test import support
import time
import unittest
import locale
import sysconfig
import sys
import platform
# Max year is only limited by the size of C int.
SIZEOF_INT = sysconfig.get_config_var('SIZEOF_INT') or 4
@ -345,6 +345,31 @@ class TimeTestCase(unittest.TestCase):
self.assertGreater(t2, t1)
self.assertAlmostEqual(dt, 0.1, delta=0.2)
def test_timestamp(self):
import decimal
calls = [
(time.time,),
(time.mktime, time.localtime()),
]
if hasattr(time, 'monotonic'):
calls.append((time.monotonic,))
if hasattr(time, 'wallclock'):
calls.append((time.wallclock,))
if hasattr(time, 'CLOCK_REALTIME'):
if hasattr(time, 'clock_gettime'):
calls.append((time.clock_gettime, time.CLOCK_REALTIME))
if hasattr(time, 'clock_getres'):
calls.append((time.clock_getres, time.CLOCK_REALTIME))
for call in calls:
func, *args = call
# test invalid timestamp
for invalid in ("int", decimal.Context):
self.assertRaises(ValueError, func, *args, timestamp=invalid)
for type in (int, float, decimal.Decimal):
self.assertIsInstance(func(*args, timestamp=type), type)
def test_wallclock(self):
t1 = time.wallclock()
t2 = time.wallclock()

View File

@ -1702,6 +1702,12 @@ stat_float_times(PyObject* self, PyObject *args)
int newval = -1;
if (!PyArg_ParseTuple(args, "|i:stat_float_times", &newval))
return NULL;
if (PyErr_WarnEx(PyExc_DeprecationWarning,
"os.stat_float_times() has been deprecated, "
"use timestamp argument of os.stat() instead",
1))
return NULL;
if (newval == -1)
/* Return old value */
return PyBool_FromLong(_stat_float_times);
@ -1711,9 +1717,12 @@ stat_float_times(PyObject* self, PyObject *args)
}
static void
fill_time(PyObject *v, int index, time_t sec, unsigned long nsec)
fill_time(PyObject *v, int index, time_t sec, unsigned long nsec,
int has_nsec, PyObject *timestamp)
{
PyObject *fval,*ival;
_PyTime_t ts;
#if SIZEOF_TIME_T > SIZEOF_LONG
ival = PyLong_FromLongLong((PY_LONG_LONG)sec);
#else
@ -1721,9 +1730,21 @@ fill_time(PyObject *v, int index, time_t sec, unsigned long nsec)
#endif
if (!ival)
return;
if (_stat_float_times) {
fval = PyFloat_FromDouble(sec + 1e-9*nsec);
} else {
if (timestamp == NULL && _stat_float_times)
timestamp = (PyObject*)&PyFloat_Type;
if (timestamp != NULL) {
ts.seconds = sec;
if (has_nsec) {
ts.numerator = nsec;
ts.denominator = 1000000000;
}
else {
ts.numerator = 0;
ts.denominator = 1;
}
fval = _PyTime_Convert(&ts, timestamp);
}
else {
fval = ival;
Py_INCREF(fval);
}
@ -1734,9 +1755,14 @@ fill_time(PyObject *v, int index, time_t sec, unsigned long nsec)
/* pack a system stat C structure into the Python stat tuple
(used by posix_stat() and posix_fstat()) */
static PyObject*
_pystat_fromstructstat(STRUCT_STAT *st)
_pystat_fromstructstat(STRUCT_STAT *st, PyObject *timestamp)
{
unsigned long ansec, mnsec, cnsec;
int has_nsec;
#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
_PyTime_t ts;
#endif
PyObject *v = PyStructSequence_New(&StatResultType);
if (v == NULL)
return NULL;
@ -1768,20 +1794,24 @@ _pystat_fromstructstat(STRUCT_STAT *st)
ansec = st->st_atim.tv_nsec;
mnsec = st->st_mtim.tv_nsec;
cnsec = st->st_ctim.tv_nsec;
has_nsec = 1;
#elif defined(HAVE_STAT_TV_NSEC2)
ansec = st->st_atimespec.tv_nsec;
mnsec = st->st_mtimespec.tv_nsec;
cnsec = st->st_ctimespec.tv_nsec;
has_nsec = 1;
#elif defined(HAVE_STAT_NSEC)
ansec = st->st_atime_nsec;
mnsec = st->st_mtime_nsec;
cnsec = st->st_ctime_nsec;
has_nsec = 1;
#else
ansec = mnsec = cnsec = 0;
has_nsec = 0;
#endif
fill_time(v, 7, st->st_atime, ansec);
fill_time(v, 8, st->st_mtime, mnsec);
fill_time(v, 9, st->st_ctime, cnsec);
fill_time(v, 7, st->st_atime, ansec, has_nsec, timestamp);
fill_time(v, 8, st->st_mtime, mnsec, has_nsec, timestamp);
fill_time(v, 9, st->st_ctime, cnsec, has_nsec, timestamp);
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
PyStructSequence_SET_ITEM(v, ST_BLKSIZE_IDX,
@ -1802,17 +1832,22 @@ _pystat_fromstructstat(STRUCT_STAT *st)
#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
{
PyObject *val;
unsigned long bsec,bnsec;
bsec = (long)st->st_birthtime;
ts.seconds = (long)st->st_birthtime;
#ifdef HAVE_STAT_TV_NSEC2
bnsec = st->st_birthtimespec.tv_nsec;
ts.numerator = st->st_birthtimespec.tv_nsec;
ts.denominator = 1000000000;
#else
bnsec = 0;
ts.numerator = 0;
ts.denominator = 1;
#endif
if (_stat_float_times) {
val = PyFloat_FromDouble(bsec + 1e-9*bnsec);
} else {
val = PyLong_FromLong((long)bsec);
if (timestamp == NULL) {
if (_stat_float_times)
val = _PyTime_Convert(&ts, (PyObject*)&PyFloat_Type);
else
val = _PyTime_Convert(&ts, (PyObject*)&PyLong_Type);
}
else {
val = _PyTime_Convert(&ts, timestamp);
}
PyStructSequence_SET_ITEM(v, ST_BIRTHTIME_IDX,
val);
@ -1832,7 +1867,7 @@ _pystat_fromstructstat(STRUCT_STAT *st)
}
static PyObject *
posix_do_stat(PyObject *self, PyObject *args,
posix_do_stat(PyObject *self, PyObject *args, PyObject *kw,
char *format,
#ifdef __VMS
int (*statfunc)(const char *, STRUCT_STAT *, ...),
@ -1842,15 +1877,18 @@ posix_do_stat(PyObject *self, PyObject *args,
char *wformat,
int (*wstatfunc)(const wchar_t *, STRUCT_STAT *))
{
static char *kwlist[] = {"path", "timestamp", NULL};
STRUCT_STAT st;
PyObject *opath;
char *path;
int res;
PyObject *result;
PyObject *timestamp = NULL;
#ifdef MS_WINDOWS
PyObject *po;
if (PyArg_ParseTuple(args, wformat, &po)) {
if (PyArg_ParseTupleAndKeywords(args, kw, wformat, kwlist,
&po, &timestamp)) {
wchar_t *wpath = PyUnicode_AsUnicode(po);
if (wpath == NULL)
return NULL;
@ -1861,15 +1899,17 @@ posix_do_stat(PyObject *self, PyObject *args,
if (res != 0)
return win32_error_object("stat", po);
return _pystat_fromstructstat(&st);
return _pystat_fromstructstat(&st, timestamp);
}
/* Drop the argument parsing error as narrow strings
are also valid. */
PyErr_Clear();
timestamp = NULL;
#endif
if (!PyArg_ParseTuple(args, format,
PyUnicode_FSConverter, &opath))
if (!PyArg_ParseTupleAndKeywords(args, kw, format, kwlist,
PyUnicode_FSConverter, &opath,
&timestamp))
return NULL;
#ifdef MS_WINDOWS
if (win32_warn_bytes_api()) {
@ -1890,7 +1930,7 @@ posix_do_stat(PyObject *self, PyObject *args,
#endif
}
else
result = _pystat_fromstructstat(&st);
result = _pystat_fromstructstat(&st, timestamp);
Py_DECREF(opath);
return result;
@ -3381,16 +3421,16 @@ posix_rmdir(PyObject *self, PyObject *args)
PyDoc_STRVAR(posix_stat__doc__,
"stat(path) -> stat result\n\n\
"stat(path, timestamp=None) -> stat result\n\n\
Perform a stat system call on the given path.");
static PyObject *
posix_stat(PyObject *self, PyObject *args)
posix_stat(PyObject *self, PyObject *args, PyObject *kw)
{
#ifdef MS_WINDOWS
return posix_do_stat(self, args, "O&:stat", STAT, "U:stat", win32_stat_w);
return posix_do_stat(self, args, kw, "O&|O:stat", STAT, "U|O:stat", win32_stat_w);
#else
return posix_do_stat(self, args, "O&:stat", STAT, NULL, NULL);
return posix_do_stat(self, args, kw, "O&|O:stat", STAT, NULL, NULL);
#endif
}
@ -6118,11 +6158,12 @@ posix_setgroups(PyObject *self, PyObject *groups)
#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4)
static PyObject *
wait_helper(pid_t pid, int status, struct rusage *ru)
wait_helper(pid_t pid, int status, struct rusage *ru, PyObject *timestamp)
{
PyObject *result;
static PyObject *struct_rusage;
_Py_IDENTIFIER(struct_rusage);
_PyTime_t ts;
if (pid == -1)
return posix_error();
@ -6146,10 +6187,17 @@ wait_helper(pid_t pid, int status, struct rusage *ru)
#define doubletime(TV) ((double)(TV).tv_sec + (TV).tv_usec * 0.000001)
#endif
ts.seconds = ru->ru_utime.tv_sec;
ts.numerator = ru->ru_utime.tv_usec;
ts.denominator = 1000000;
PyStructSequence_SET_ITEM(result, 0,
PyFloat_FromDouble(doubletime(ru->ru_utime)));
_PyTime_Convert(&ts, timestamp));
ts.seconds = ru->ru_stime.tv_sec;
ts.numerator = ru->ru_stime.tv_usec;
ts.denominator = 1000000;
PyStructSequence_SET_ITEM(result, 1,
PyFloat_FromDouble(doubletime(ru->ru_stime)));
_PyTime_Convert(&ts, timestamp));
#define SET_INT(result, index, value)\
PyStructSequence_SET_ITEM(result, index, PyLong_FromLong(value))
SET_INT(result, 2, ru->ru_maxrss);
@ -6179,51 +6227,55 @@ wait_helper(pid_t pid, int status, struct rusage *ru)
#ifdef HAVE_WAIT3
PyDoc_STRVAR(posix_wait3__doc__,
"wait3(options) -> (pid, status, rusage)\n\n\
"wait3(options[, timestamp=float]) -> (pid, status, rusage)\n\n\
Wait for completion of a child process.");
static PyObject *
posix_wait3(PyObject *self, PyObject *args)
posix_wait3(PyObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = {"options", "timestamp", NULL};
pid_t pid;
int options;
struct rusage ru;
WAIT_TYPE status;
WAIT_STATUS_INT(status) = 0;
PyObject *timestamp = NULL;
if (!PyArg_ParseTuple(args, "i:wait3", &options))
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|O:wait3", kwlist, &options, &timestamp))
return NULL;
Py_BEGIN_ALLOW_THREADS
pid = wait3(&status, options, &ru);
Py_END_ALLOW_THREADS
return wait_helper(pid, WAIT_STATUS_INT(status), &ru);
return wait_helper(pid, WAIT_STATUS_INT(status), &ru, timestamp);
}
#endif /* HAVE_WAIT3 */
#ifdef HAVE_WAIT4
PyDoc_STRVAR(posix_wait4__doc__,
"wait4(pid, options) -> (pid, status, rusage)\n\n\
"wait4(pid, options[, timestamp=float]) -> (pid, status, rusage)\n\n\
Wait for completion of a given child process.");
static PyObject *
posix_wait4(PyObject *self, PyObject *args)
posix_wait4(PyObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = {"pid", "options", "timestamp", NULL};
pid_t pid;
int options;
struct rusage ru;
WAIT_TYPE status;
WAIT_STATUS_INT(status) = 0;
PyObject *timestamp = NULL;
if (!PyArg_ParseTuple(args, _Py_PARSE_PID "i:wait4", &pid, &options))
if (!PyArg_ParseTupleAndKeywords(args, kwargs, _Py_PARSE_PID "i|O:wait4", kwlist, &pid, &options, &timestamp))
return NULL;
Py_BEGIN_ALLOW_THREADS
pid = wait4(pid, &status, options, &ru);
Py_END_ALLOW_THREADS
return wait_helper(pid, WAIT_STATUS_INT(status), &ru);
return wait_helper(pid, WAIT_STATUS_INT(status), &ru, timestamp);
}
#endif /* HAVE_WAIT4 */
@ -6350,20 +6402,20 @@ posix_wait(PyObject *self, PyObject *noargs)
PyDoc_STRVAR(posix_lstat__doc__,
"lstat(path) -> stat result\n\n\
"lstat(path, timestamp=None) -> stat result\n\n\
Like stat(path), but do not follow symbolic links.");
static PyObject *
posix_lstat(PyObject *self, PyObject *args)
posix_lstat(PyObject *self, PyObject *args, PyObject *kw)
{
#ifdef HAVE_LSTAT
return posix_do_stat(self, args, "O&:lstat", lstat, NULL, NULL);
return posix_do_stat(self, args, kw, "O&|O:lstat", lstat, NULL, NULL);
#else /* !HAVE_LSTAT */
#ifdef MS_WINDOWS
return posix_do_stat(self, args, "O&:lstat", win32_lstat, "U:lstat",
return posix_do_stat(self, args, kw, "O&|O:lstat", win32_lstat, "U|O:lstat",
win32_lstat_w);
#else
return posix_do_stat(self, args, "O&:lstat", STAT, NULL, NULL);
return posix_do_stat(self, args, "kw, O&|O:lstat", STAT, NULL, NULL);
#endif
#endif /* !HAVE_LSTAT */
}
@ -7322,16 +7374,19 @@ done:
#endif
PyDoc_STRVAR(posix_fstat__doc__,
"fstat(fd) -> stat result\n\n\
"fstat(fd, timestamp=None) -> stat result\n\n\
Like stat(), but for an open file descriptor.");
static PyObject *
posix_fstat(PyObject *self, PyObject *args)
posix_fstat(PyObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = {"fd", "timestamp", NULL};
int fd;
STRUCT_STAT st;
int res;
if (!PyArg_ParseTuple(args, "i:fstat", &fd))
PyObject *timestamp = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|O:fstat", kwlist,
&fd, &timestamp))
return NULL;
#ifdef __VMS
/* on OpenVMS we must ensure that all bytes are written to the file */
@ -7350,7 +7405,7 @@ posix_fstat(PyObject *self, PyObject *args)
#endif
}
return _pystat_fromstructstat(&st);
return _pystat_fromstructstat(&st, timestamp);
}
PyDoc_STRVAR(posix_isatty__doc__,
@ -9634,22 +9689,25 @@ posix_fchownat(PyObject *self, PyObject *args)
#ifdef HAVE_FSTATAT
PyDoc_STRVAR(posix_fstatat__doc__,
"fstatat(dirfd, path, flags=0) -> stat result\n\n\
"fstatat(dirfd, path, flags=0, timestamp=None) -> stat result\n\n\
Like stat() but if path is relative, it is taken as relative to dirfd.\n\
flags is optional and may be 0 or AT_SYMLINK_NOFOLLOW.\n\
If path is relative and dirfd is the special value AT_FDCWD, then path\n\
is interpreted relative to the current working directory.");
static PyObject *
posix_fstatat(PyObject *self, PyObject *args)
posix_fstatat(PyObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = {"dirfd", "path", "flags", "timestamp", NULL};
PyObject *opath;
char *path;
STRUCT_STAT st;
int dirfd, res, flags = 0;
PyObject *timestamp = NULL;
if (!PyArg_ParseTuple(args, "iO&|i:fstatat",
&dirfd, PyUnicode_FSConverter, &opath, &flags))
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iO&|iO:fstatat", kwlist,
&dirfd, PyUnicode_FSConverter, &opath,
&flags, &timestamp))
return NULL;
path = PyBytes_AsString(opath);
@ -9660,7 +9718,7 @@ posix_fstatat(PyObject *self, PyObject *args)
if (res != 0)
return posix_error();
return _pystat_fromstructstat(&st);
return _pystat_fromstructstat(&st, timestamp);
}
#endif
@ -10524,7 +10582,7 @@ static PyMethodDef posix_methods[] = {
#ifdef HAVE_FDOPENDIR
{"flistdir", posix_flistdir, METH_VARARGS, posix_flistdir__doc__},
#endif
{"lstat", posix_lstat, METH_VARARGS, posix_lstat__doc__},
{"lstat", (PyCFunction)posix_lstat, METH_VARARGS | METH_KEYWORDS, posix_lstat__doc__},
{"mkdir", posix_mkdir, METH_VARARGS, posix_mkdir__doc__},
#ifdef HAVE_NICE
{"nice", posix_nice, METH_VARARGS, posix_nice__doc__},
@ -10544,7 +10602,8 @@ static PyMethodDef posix_methods[] = {
{"rename", posix_rename, METH_VARARGS, posix_rename__doc__},
{"replace", posix_replace, METH_VARARGS, posix_replace__doc__},
{"rmdir", posix_rmdir, METH_VARARGS, posix_rmdir__doc__},
{"stat", posix_stat, METH_VARARGS, posix_stat__doc__},
{"stat", (PyCFunction)posix_stat,
METH_VARARGS | METH_KEYWORDS, posix_stat__doc__},
{"stat_float_times", stat_float_times, METH_VARARGS, stat_float_times__doc__},
#if defined(HAVE_SYMLINK) && !defined(MS_WINDOWS)
{"symlink", posix_symlink, METH_VARARGS, posix_symlink__doc__},
@ -10705,10 +10764,12 @@ static PyMethodDef posix_methods[] = {
{"wait", posix_wait, METH_NOARGS, posix_wait__doc__},
#endif /* HAVE_WAIT */
#ifdef HAVE_WAIT3
{"wait3", posix_wait3, METH_VARARGS, posix_wait3__doc__},
{"wait3", (PyCFunction)posix_wait3,
METH_VARARGS | METH_KEYWORDS, posix_wait3__doc__},
#endif /* HAVE_WAIT3 */
#ifdef HAVE_WAIT4
{"wait4", posix_wait4, METH_VARARGS, posix_wait4__doc__},
{"wait4", (PyCFunction)posix_wait4,
METH_VARARGS | METH_KEYWORDS, posix_wait4__doc__},
#endif /* HAVE_WAIT4 */
#if defined(HAVE_WAITID) && !defined(__APPLE__)
{"waitid", posix_waitid, METH_VARARGS, posix_waitid__doc__},
@ -10759,7 +10820,8 @@ static PyMethodDef posix_methods[] = {
{"sendfile", (PyCFunction)posix_sendfile, METH_VARARGS | METH_KEYWORDS,
posix_sendfile__doc__},
#endif
{"fstat", posix_fstat, METH_VARARGS, posix_fstat__doc__},
{"fstat", (PyCFunction)posix_fstat, METH_VARARGS | METH_KEYWORDS,
posix_fstat__doc__},
{"isatty", posix_isatty, METH_VARARGS, posix_isatty__doc__},
#ifdef HAVE_PIPE
{"pipe", posix_pipe, METH_NOARGS, posix_pipe__doc__},
@ -10894,7 +10956,8 @@ static PyMethodDef posix_methods[] = {
{"fchownat", posix_fchownat, METH_VARARGS, posix_fchownat__doc__},
#endif /* HAVE_FCHOWNAT */
#ifdef HAVE_FSTATAT
{"fstatat", posix_fstatat, METH_VARARGS, posix_fstatat__doc__},
{"fstatat", (PyCFunction)posix_fstatat, METH_VARARGS | METH_KEYWORDS,
posix_fstatat__doc__},
#endif
#ifdef HAVE_FUTIMESAT
{"futimesat", posix_futimesat, METH_VARARGS, posix_futimesat__doc__},

View File

@ -40,24 +40,30 @@
#include <sys/time.h>
#endif
#if (defined(MS_WINDOWS) && !defined(__BORLANDC__)) || defined(HAVE_CLOCK)
# define HAVE_PYCLOCK
#endif
/* Forward declarations */
static int floatsleep(double);
static double floattime(void);
static PyObject *
time_time(PyObject *self, PyObject *unused)
time_time(PyObject *self, PyObject *args, PyObject *kwargs)
{
double secs;
secs = floattime();
if (secs == 0.0) {
PyErr_SetFromErrno(PyExc_IOError);
static char *kwlist[] = {"timestamp", NULL};
PyObject *timestamp = NULL;
_PyTime_t ts;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O:time", kwlist,
&timestamp))
return NULL;
}
return PyFloat_FromDouble(secs);
_PyTime_get(&ts);
return _PyTime_Convert(&ts, timestamp);
}
PyDoc_STRVAR(time_doc,
"time() -> floating point number\n\
"time(timestamp=float) -> floating point number\n\
\n\
Return the current time in seconds since the Epoch.\n\
Fractions of a second may be present if the system clock provides them.");
@ -72,65 +78,91 @@ Fractions of a second may be present if the system clock provides them.");
#endif
#endif
static PyObject *
pyclock(void)
static int
pyclock(_PyTime_t *ts)
{
clock_t value;
value = clock();
if (value == (clock_t)-1) {
clock_t processor_time;
processor_time = clock();
if (processor_time == (clock_t)-1) {
PyErr_SetString(PyExc_RuntimeError,
"the processor time used is not available "
"or its value cannot be represented");
return NULL;
return -1;
}
return PyFloat_FromDouble((double)value / CLOCKS_PER_SEC);
ts->seconds = 0;
assert(sizeof(clock_t) <= sizeof(_PyTime_fraction_t));
ts->numerator = Py_SAFE_DOWNCAST(processor_time,
clock_t, _PyTime_fraction_t);
ts->denominator = CLOCKS_PER_SEC;
return 0;
}
#endif /* HAVE_CLOCK */
#if defined(MS_WINDOWS) && !defined(__BORLANDC__)
/* Win32 has better clock replacement; we have our own version, due to Mark
Hammond and Tim Peters */
static PyObject *
win32_clock(int fallback)
static int
win32_clock(_PyTime_t *ts, int fallback)
{
static LONGLONG cpu_frequency = 0;
static LONGLONG ctrStart;
static LONGLONG start;
LARGE_INTEGER now;
double diff;
LONGLONG dt;
if (cpu_frequency == 0) {
LARGE_INTEGER freq;
QueryPerformanceCounter(&now);
ctrStart = now.QuadPart;
start = now.QuadPart;
if (!QueryPerformanceFrequency(&freq) || freq.QuadPart == 0) {
/* Unlikely to happen - this works on all intel
machines at least! Revert to clock() */
if (fallback)
return pyclock();
else
return PyErr_SetFromWindowsErr(0);
if (fallback) {
return pyclock(ts);
}
else {
PyErr_SetFromWindowsErr(0);
return -1;
}
}
cpu_frequency = freq.QuadPart;
}
QueryPerformanceCounter(&now);
diff = (double)(now.QuadPart - ctrStart);
return PyFloat_FromDouble(diff / (double)cpu_frequency);
dt = now.QuadPart - start;
ts->seconds = 0;
assert(sizeof(LONGLONG) <= sizeof(_PyTime_fraction_t));
ts->numerator = Py_SAFE_DOWNCAST(dt,
LONGLONG, _PyTime_fraction_t);
ts->denominator = Py_SAFE_DOWNCAST(cpu_frequency,
LONGLONG, _PyTime_fraction_t);
return 0;
}
#endif
#if (defined(MS_WINDOWS) && !defined(__BORLANDC__)) || defined(HAVE_CLOCK)
static PyObject *
time_clock(PyObject *self, PyObject *unused)
time_clock(PyObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = {"timestamp", NULL};
_PyTime_t ts;
PyObject *timestamp = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O:clock", kwlist,
&timestamp))
return NULL;
#if defined(MS_WINDOWS) && !defined(__BORLANDC__)
return win32_clock(1);
if (win32_clock(&ts, 1) == -1)
return NULL;
#else
return pyclock();
if (pyclock(&ts) == -1)
return NULL;
#endif
return _PyTime_Convert(&ts, timestamp);
}
PyDoc_STRVAR(clock_doc,
"clock() -> floating point number\n\
"clock(timestamp=float) -> floating point number\n\
\n\
Return the CPU time or real time since the start of the process or since\n\
the first call to clock(). This has as much precision as the system\n\
@ -139,13 +171,17 @@ records.");
#ifdef HAVE_CLOCK_GETTIME
static PyObject *
time_clock_gettime(PyObject *self, PyObject *args)
time_clock_gettime(PyObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = {"clk_id", "timestamp", NULL};
PyObject *timestamp = NULL;
int ret;
clockid_t clk_id;
struct timespec tp;
_PyTime_t ts;
if (!PyArg_ParseTuple(args, "i:clock_gettime", &clk_id))
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|O:clock_gettime", kwlist,
&clk_id, &timestamp))
return NULL;
ret = clock_gettime((clockid_t)clk_id, &tp);
@ -153,25 +189,31 @@ time_clock_gettime(PyObject *self, PyObject *args)
PyErr_SetFromErrno(PyExc_IOError);
return NULL;
}
return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
ts.seconds = tp.tv_sec;
ts.numerator = tp.tv_nsec;
ts.denominator = 1000000000;
return _PyTime_Convert(&ts, timestamp);
}
PyDoc_STRVAR(clock_gettime_doc,
"clock_gettime(clk_id) -> floating point number\n\
"clock_gettime(clk_id, timestamp=float) -> floating point number\n\
\n\
Return the time of the specified clock clk_id.");
#endif
#ifdef HAVE_CLOCK_GETRES
static PyObject *
time_clock_getres(PyObject *self, PyObject *args)
time_clock_getres(PyObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = {"clk_id", "timestamp", NULL};
PyObject *timestamp = NULL;
int ret;
clockid_t clk_id;
struct timespec tp;
_PyTime_t ts;
if (!PyArg_ParseTuple(args, "i:clock_getres", &clk_id))
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|O:clock_getres", kwlist,
&clk_id, &timestamp))
return NULL;
ret = clock_getres((clockid_t)clk_id, &tp);
@ -179,12 +221,14 @@ time_clock_getres(PyObject *self, PyObject *args)
PyErr_SetFromErrno(PyExc_IOError);
return NULL;
}
return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
ts.seconds = tp.tv_sec;
ts.numerator = tp.tv_nsec;
ts.denominator = 1000000000;
return _PyTime_Convert(&ts, timestamp);
}
PyDoc_STRVAR(clock_getres_doc,
"clock_getres(clk_id) -> floating point number\n\
"clock_getres(clk_id, timestamp=float) -> floating point number\n\
\n\
Return the resolution (precision) of the specified clock clk_id.");
#endif
@ -707,10 +751,19 @@ not present, current time as returned by localtime() is used.");
#ifdef HAVE_MKTIME
static PyObject *
time_mktime(PyObject *self, PyObject *tup)
time_mktime(PyObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = {"t", "timestamp", NULL};
PyObject *timestamp = NULL;
PyObject *tup;
struct tm buf;
time_t tt;
_PyTime_t ts;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:mktime", kwlist,
&tup, &timestamp))
return NULL;
if (!gettmarg(tup, &buf))
return NULL;
buf.tm_wday = -1; /* sentinel; original value ignored */
@ -722,7 +775,10 @@ time_mktime(PyObject *self, PyObject *tup)
"mktime argument out of range");
return NULL;
}
return PyFloat_FromDouble((double)tt);
ts.seconds = tt;
ts.numerator = 0;
ts.denominator = 1;
return _PyTime_Convert(&ts, timestamp);
}
PyDoc_STRVAR(mktime_doc,
@ -768,12 +824,14 @@ the local timezone used by methods such as localtime, but this behaviour\n\
should not be relied on.");
#endif /* HAVE_WORKING_TZSET */
static PyObject *
time_wallclock(PyObject *self, PyObject *unused)
static int
pywallclock(_PyTime_t *ts)
{
#if defined(MS_WINDOWS) && !defined(__BORLANDC__)
return win32_clock(1);
#elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
return win32_clock(ts, 1);
#else
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
static int clk_index = 0;
clockid_t clk_ids[] = {
#ifdef CLOCK_MONOTONIC_RAW
@ -793,20 +851,41 @@ time_wallclock(PyObject *self, PyObject *unused)
clockid_t clk_id = clk_ids[clk_index];
ret = clock_gettime(clk_id, &tp);
if (ret == 0)
return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
{
ts->seconds = tp.tv_sec;
ts->numerator = tp.tv_nsec;
ts->denominator = 1000000000;
return 0;
}
clk_index++;
if (Py_ARRAY_LENGTH(clk_ids) <= clk_index)
clk_index = -1;
}
return time_time(self, NULL);
#else
return time_time(self, NULL);
#endif /* defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) */
_PyTime_get(ts);
return 0;
#endif
}
static PyObject *
time_wallclock(PyObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = {"timestamp", NULL};
PyObject *timestamp = NULL;
_PyTime_t ts;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O:wallclock", kwlist,
&timestamp))
return NULL;
if (pywallclock(&ts))
return NULL;
return _PyTime_Convert(&ts, timestamp);
}
PyDoc_STRVAR(wallclock_doc,
"wallclock() -> float\n\
"wallclock(timestamp=float)\n\
\n\
Return the current time in fractions of a second to the system's best\n\
ability. Use this when the most accurate representation of wall-clock is\n\
@ -821,11 +900,11 @@ calls is valid.");
#ifdef HAVE_PYTIME_MONOTONIC
static PyObject *
time_monotonic(PyObject *self, PyObject *unused)
time_monotonic(PyObject *self, PyObject *args, PyObject *kwargs)
{
#if defined(MS_WINDOWS) && !defined(__BORLANDC__)
return win32_clock(0);
#else
static char *kwlist[] = {"timestamp", NULL};
PyObject *timestamp = NULL;
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
static int clk_index = 0;
clockid_t clk_ids[] = {
#ifdef CLOCK_MONOTONIC_RAW
@ -835,12 +914,28 @@ time_monotonic(PyObject *self, PyObject *unused)
};
int ret;
struct timespec tp;
#endif
_PyTime_t ts;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O:monotonic", kwlist,
&timestamp))
return NULL;
#if defined(MS_WINDOWS) && !defined(__BORLANDC__)
if (win32_clock(&ts, 0) == -1)
return NULL;
return _PyTime_Convert(&ts, timestamp);
#else
while (0 <= clk_index) {
clockid_t clk_id = clk_ids[clk_index];
ret = clock_gettime(clk_id, &tp);
if (ret == 0)
return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
{
ts.seconds = tp.tv_sec;
ts.numerator = tp.tv_nsec;
ts.denominator = 1000000000;
return _PyTime_Convert(&ts, timestamp);
}
clk_index++;
if (Py_ARRAY_LENGTH(clk_ids) <= clk_index)
@ -968,15 +1063,19 @@ PyInit_timezone(PyObject *m) {
static PyMethodDef time_methods[] = {
{"time", time_time, METH_NOARGS, time_doc},
#if (defined(MS_WINDOWS) && !defined(__BORLANDC__)) || defined(HAVE_CLOCK)
{"clock", time_clock, METH_NOARGS, clock_doc},
{"time", (PyCFunction)time_time,
METH_VARARGS | METH_KEYWORDS, time_doc},
#ifdef HAVE_PYCLOCK
{"clock", (PyCFunction)time_clock,
METH_VARARGS | METH_KEYWORDS, clock_doc},
#endif
#ifdef HAVE_CLOCK_GETTIME
{"clock_gettime", time_clock_gettime, METH_VARARGS, clock_gettime_doc},
{"clock_gettime", (PyCFunction)time_clock_gettime,
METH_VARARGS | METH_KEYWORDS, clock_gettime_doc},
#endif
#ifdef HAVE_CLOCK_GETRES
{"clock_getres", time_clock_getres, METH_VARARGS, clock_getres_doc},
{"clock_getres", (PyCFunction)time_clock_getres,
METH_VARARGS | METH_KEYWORDS, clock_getres_doc},
#endif
{"sleep", time_sleep, METH_VARARGS, sleep_doc},
{"gmtime", time_gmtime, METH_VARARGS, gmtime_doc},
@ -984,10 +1083,12 @@ static PyMethodDef time_methods[] = {
{"asctime", time_asctime, METH_VARARGS, asctime_doc},
{"ctime", time_ctime, METH_VARARGS, ctime_doc},
#ifdef HAVE_MKTIME
{"mktime", time_mktime, METH_O, mktime_doc},
{"mktime", (PyCFunction)time_mktime,
METH_VARARGS | METH_KEYWORDS, mktime_doc},
#endif
#ifdef HAVE_PYTIME_MONOTONIC
{"monotonic", time_monotonic, METH_NOARGS, monotonic_doc},
{"monotonic", (PyCFunction)time_monotonic,
METH_VARARGS | METH_KEYWORDS, monotonic_doc},
#endif
#ifdef HAVE_STRFTIME
{"strftime", time_strftime, METH_VARARGS, strftime_doc},
@ -996,7 +1097,8 @@ static PyMethodDef time_methods[] = {
#ifdef HAVE_WORKING_TZSET
{"tzset", time_tzset, METH_NOARGS, tzset_doc},
#endif
{"wallclock", time_wallclock, METH_NOARGS, wallclock_doc},
{"wallclock", (PyCFunction)time_wallclock,
METH_VARARGS | METH_KEYWORDS, wallclock_doc},
{NULL, NULL} /* sentinel */
};
@ -1081,15 +1183,6 @@ PyInit_time(void)
return m;
}
static double
floattime(void)
{
_PyTime_timeval t;
_PyTime_gettimeofday(&t);
return (double)t.tv_sec + t.tv_usec*0.000001;
}
/* Implement floatsleep() for various platforms.
When interrupted (or when another error occurs), return -1 and
set an exception; else return 0. */

View File

@ -18,24 +18,36 @@
extern int ftime(struct timeb *);
#endif
#define MICROSECONDS 1000000
void
_PyTime_gettimeofday(_PyTime_timeval *tp)
_PyTime_get(_PyTime_t *ts)
{
#ifdef MS_WINDOWS
FILETIME system_time;
ULARGE_INTEGER large;
ULONGLONG microseconds;
ULONGLONG value;
GetSystemTimeAsFileTime(&system_time);
large.u.LowPart = system_time.dwLowDateTime;
large.u.HighPart = system_time.dwHighDateTime;
/* 11,644,473,600,000,000: number of microseconds between
/* 116,444,736,000,000,000: number of 100 ns between
the 1st january 1601 and the 1st january 1970 (369 years + 89 leap
days). */
microseconds = large.QuadPart / 10 - 11644473600000000;
tp->tv_sec = microseconds / 1000000;
tp->tv_usec = microseconds % 1000000;
value = large.QuadPart - 116444736000000000;
ts->seconds = 0;
ts->numerator = value;
ts->denominator = (_PyTime_fraction_t)10000000;
#else
#ifdef HAVE_GETTIMEOFDAY
struct timeval tv;
int err;
#endif
#if defined(HAVE_FTIME)
struct timeb t;
#endif
/* There are three ways to get the time:
(1) gettimeofday() -- resolution in microseconds
(2) ftime() -- resolution in milliseconds
@ -47,29 +59,324 @@ _PyTime_gettimeofday(_PyTime_timeval *tp)
#ifdef HAVE_GETTIMEOFDAY
#ifdef GETTIMEOFDAY_NO_TZ
if (gettimeofday(tp) == 0)
return;
err = gettimeofday(&tv);
#else /* !GETTIMEOFDAY_NO_TZ */
if (gettimeofday(tp, (struct timezone *)NULL) == 0)
return;
err = gettimeofday(&tv, (struct timezone *)NULL);
#endif /* !GETTIMEOFDAY_NO_TZ */
if (err == 0)
{
ts->seconds = tv.tv_sec;
ts->numerator = tv.tv_usec;
ts->denominator = MICROSECONDS;
return;
}
#endif /* !HAVE_GETTIMEOFDAY */
#if defined(HAVE_FTIME)
{
struct timeb t;
ftime(&t);
tp->tv_sec = t.time;
tp->tv_usec = t.millitm * 1000;
}
ts->seconds = t.time;
ts->numerator = t.millitm;
ts->denominator = 1000;
#else /* !HAVE_FTIME */
tp->tv_sec = time(NULL);
tp->tv_usec = 0;
ts->seconds = time(NULL);
ts->numerator = 0;
ts->denominator = 1;
#endif /* !HAVE_FTIME */
#endif /* MS_WINDOWS */
}
void
_PyTime_gettimeofday(_PyTime_timeval *tv)
{
_PyTime_t ts;
_PyTime_fraction_t k;
time_t sec;
_PyTime_get(&ts);
tv->tv_sec = ts.seconds;
if (ts.numerator) {
if (ts.numerator > ts.denominator) {
sec = Py_SAFE_DOWNCAST(ts.numerator / ts.denominator,
_PyTime_fraction_t, time_t);
/* ignore integer overflow because _PyTime_gettimeofday() has
no return value */
tv->tv_sec += sec;
ts.numerator = ts.numerator % ts.denominator;
}
if (MICROSECONDS >= ts.denominator) {
k = (_PyTime_fraction_t)MICROSECONDS / ts.denominator;
tv->tv_usec = (long)(ts.numerator * k);
}
else {
k = ts.denominator / (_PyTime_fraction_t)MICROSECONDS;
tv->tv_usec = (long)(ts.numerator / k);
}
}
else {
tv->tv_usec = 0;
}
}
static PyObject*
_PyLong_FromTime_t(time_t value)
{
#if SIZEOF_TIME_T <= SIZEOF_LONG
return PyLong_FromLong(value);
#else
assert(sizeof(time_t) <= sizeof(PY_LONG_LONG));
return PyLong_FromLongLong(value);
#endif
}
#if defined(HAVE_LONG_LONG)
# define _PyLong_FromTimeFraction_t PyLong_FromLongLong
#else
# define _PyLong_FromTimeFraction_t PyLong_FromSize_t
#endif
/* Convert a timestamp to a PyFloat object */
static PyObject*
_PyTime_AsFloat(_PyTime_t *ts)
{
double d;
d = (double)ts->seconds;
d += (double)ts->numerator / (double)ts->denominator;
return PyFloat_FromDouble(d);
}
/* Convert a timestamp to a PyLong object */
static PyObject*
_PyTime_AsLong(_PyTime_t *ts)
{
PyObject *a, *b, *c;
a = _PyLong_FromTime_t(ts->seconds);
if (a == NULL)
return NULL;
b = _PyLong_FromTimeFraction_t(ts->numerator / ts->denominator);
if (b == NULL)
{
Py_DECREF(a);
return NULL;
}
c = PyNumber_Add(a, b);
Py_DECREF(a);
Py_DECREF(b);
return c;
}
/* Convert a timestamp to a decimal.Decimal object */
static PyObject*
_PyTime_AsDecimal(_PyTime_t *ts)
{
static PyObject* module = NULL;
static PyObject* decimal = NULL;
static PyObject* exponent_context = NULL;
static PyObject* context = NULL;
/* exponent cache, dictionary of:
int (denominator) => Decimal (1/denominator) */
static PyObject* exponent_cache = NULL;
PyObject *t = NULL;
PyObject *key, *exponent, *quantized;
_Py_IDENTIFIER(quantize);
_Py_IDENTIFIER(__truediv__);
if (!module) {
module = PyImport_ImportModuleNoBlock("decimal");
if (module == NULL)
return NULL;
}
if (!decimal) {
decimal = PyObject_GetAttrString(module, "Decimal");
if (decimal == NULL)
return NULL;
}
if (context == NULL)
{
/* Use 12 decimal digits to store 10,000 years in seconds + 9
decimal digits for the floating part in nanoseconds + 1 decimal
digit to round correctly.
context = decimal.Context(22, rounding=decimal.ROUND_HALF_EVEN)
exponent_context = decimal.Context(1, rounding=decimal.ROUND_HALF_EVEN)
*/
PyObject *context_class, *rounding;
context_class = PyObject_GetAttrString(module, "Context");
if (context_class == NULL)
return NULL;
rounding = PyObject_GetAttrString(module, "ROUND_HALF_EVEN");
if (rounding == NULL)
{
Py_DECREF(context_class);
return NULL;
}
context = PyObject_CallFunction(context_class, "iO", 22, rounding);
if (context == NULL)
{
Py_DECREF(context_class);
Py_DECREF(rounding);
return NULL;
}
exponent_context = PyObject_CallFunction(context_class, "iO", 1, rounding);
Py_DECREF(context_class);
Py_DECREF(rounding);
if (exponent_context == NULL)
{
Py_CLEAR(context);
return NULL;
}
}
/* t = decimal.Decimal(value) */
if (ts->seconds) {
PyObject *f = _PyLong_FromTime_t(ts->seconds);
t = PyObject_CallFunction(decimal, "O", f);
Py_CLEAR(f);
}
else {
t = PyObject_CallFunction(decimal, "iO", 0, context);
}
if (t == NULL)
return NULL;
if (ts->numerator)
{
/* t += decimal.Decimal(numerator, ctx) / decimal.Decimal(denominator, ctx) */
PyObject *a, *b, *c, *d, *x;
x = _PyLong_FromTimeFraction_t(ts->numerator);
if (x == NULL)
goto error;
a = PyObject_CallFunction(decimal, "OO", x, context);
Py_CLEAR(x);
if (a == NULL)
goto error;
x = _PyLong_FromTimeFraction_t(ts->denominator);
if (x == NULL)
{
Py_DECREF(a);
goto error;
}
b = PyObject_CallFunction(decimal, "OO", x, context);
Py_CLEAR(x);
if (b == NULL)
{
Py_DECREF(a);
goto error;
}
c = _PyObject_CallMethodId(a, &PyId___truediv__, "OO",
b, context);
Py_DECREF(a);
Py_DECREF(b);
if (c == NULL)
goto error;
d = PyNumber_Add(t, c);
Py_DECREF(c);
if (d == NULL)
goto error;
Py_DECREF(t);
t = d;
}
if (exponent_cache == NULL) {
exponent_cache = PyDict_New();
if (exponent_cache == NULL)
goto error;
}
key = _PyLong_FromTimeFraction_t(ts->denominator);
if (key == NULL)
goto error;
exponent = PyDict_GetItem(exponent_cache, key);
if (exponent == NULL) {
/* exponent = decimal.Decimal(1) / decimal.Decimal(resolution) */
PyObject *one, *denominator;
one = PyObject_CallFunction(decimal, "i", 1);
if (one == NULL) {
Py_DECREF(key);
goto error;
}
denominator = PyObject_CallFunction(decimal, "O", key);
if (denominator == NULL) {
Py_DECREF(key);
Py_DECREF(one);
goto error;
}
exponent = _PyObject_CallMethodId(one, &PyId___truediv__, "OO",
denominator, exponent_context);
Py_DECREF(one);
Py_DECREF(denominator);
if (exponent == NULL) {
Py_DECREF(key);
goto error;
}
if (PyDict_SetItem(exponent_cache, key, exponent) < 0) {
Py_DECREF(key);
Py_DECREF(exponent);
goto error;
}
Py_DECREF(key);
}
/* t = t.quantize(exponent, None, context) */
quantized = _PyObject_CallMethodId(t, &PyId_quantize, "OOO",
exponent, Py_None, context);
if (quantized == NULL)
goto error;
Py_DECREF(t);
t = quantized;
return t;
error:
Py_XDECREF(t);
return NULL;
}
PyObject*
_PyTime_Convert(_PyTime_t *ts, PyObject *format)
{
assert(ts->denominator != 0);
if (format == NULL || (PyTypeObject *)format == &PyFloat_Type)
return _PyTime_AsFloat(ts);
if ((PyTypeObject *)format == &PyLong_Type)
return _PyTime_AsLong(ts);
if (PyType_Check(format))
{
PyObject *module, *name;
_Py_IDENTIFIER(__name__);
_Py_IDENTIFIER(__module__);
module = _PyObject_GetAttrId(format, &PyId___module__);
name = _PyObject_GetAttrId(format, &PyId___name__);
if (module != NULL && PyUnicode_Check(module)
&& name != NULL && PyUnicode_Check(name))
{
if (PyUnicode_CompareWithASCIIString(module, "decimal") == 0
&& PyUnicode_CompareWithASCIIString(name, "Decimal") == 0)
return _PyTime_AsDecimal(ts);
}
else
PyErr_Clear();
}
PyErr_Format(PyExc_ValueError, "Unknown timestamp format: %R", format);
return NULL;
}
void
_PyTime_Init()
{