[issue13882] Add format argument for time.time(), time.clock(), ... to get a timestamp as a Decimal object

STINNER Victor report at bugs.python.org
Tue Jan 31 13:00:25 CET 2012


STINNER Victor <victor.stinner at haypocalc.com> added the comment:

Patch version 6:

 - timestamp format is now a type instead of a string, e.g. time.time(int)
 - add int and datetime.timedelta formats, remove timespec format
 - complete the documentation
 - fix integer overflows, convert correctly time_t to PyLong

There are now 5 timestamp formats:

 - int
 - float
 - decimal.Decimal
 - datetime.datetime
 - datetime.timedelta

I consider the patch as ready to be commited, or at least ready for a
review ;-) There is no more FIXME or known limitation. Well, now the
most important part is to decide the API and the list of timestamp
formats.

The patch should be tested on Linux, FreeBSD and Windows, 32 and 64
bits to check assertions on type sizes:

assert(sizeof(clock_t) <= sizeof(size_t));
assert(sizeof(LONGLONG) <= sizeof(size_t));
assert(sizeof(time_t) <= sizeof(PY_LONG_LONG));

----------
Added file: http://bugs.python.org/file24378/time_decimal-6.patch

_______________________________________
Python tracker <report at bugs.python.org>
<http://bugs.python.org/issue13882>
_______________________________________
-------------- next part --------------
diff --git a/Doc/library/os.rst b/Doc/library/os.rst
--- a/Doc/library/os.rst
+++ b/Doc/library/os.rst
@@ -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])
 
@@ -1955,7 +1961,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`.)
@@ -1975,6 +1981,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` values type is
+   :class:`float` by default, or :class:`int` if :func:`os.stat_float_times` is
+   ``False``. Set the optional *timestamp* argument to get another
+   :ref:`timestamp format <timestamp-formats>`.
+
    On some Unix systems (such as Linux), the following attributes may also be
    available:
 
@@ -2030,6 +2041,9 @@ Files and Directories
 
    Availability: Unix, Windows.
 
+   .. versionchanged:: 3.3
+      Added the *timestamp* argument.
+
 
 .. function:: stat_float_times([newvalue])
 
@@ -2055,6 +2069,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)
 
diff --git a/Doc/library/time.rst b/Doc/library/time.rst
--- a/Doc/library/time.rst
+++ b/Doc/library/time.rst
@@ -95,6 +95,16 @@ An explanation of some terminology and c
   | local time              |                         |                         |
   +-------------------------+-------------------------+-------------------------+
 
+.. _timestamp-formats:
+
+* Python supports the following timestamp formats:
+
+  * :class:`int`
+  * :class:`float`
+  * :class:`datetime.datetime`
+  * :class:`datetime.timedelta`
+  * :class:`decimal.Decimal`
+
 
 The module defines the following functions and data items:
 
@@ -118,7 +128,7 @@ The module defines the following functio
       Unlike the C function of the same name, there is no trailing newline.
 
 
-.. function:: clock()
+.. function:: clock(format=float)
 
    .. index::
       single: CPU time
@@ -135,16 +145,27 @@ The module defines the following functio
    :c:func:`QueryPerformanceCounter`. The resolution is typically better than one
    microsecond.
 
+   Return the time as a floating point number by default, set the optional
+   *format* argument to get another :ref:`timestamp format <timestamp-formats>`.
 
-.. function:: clock_getres(clk_id)
+   .. versionchanged:: 3.3
+      Added the *format* argument.
+
+
+.. function:: clock_getres(clk_id, format=float)
 
    Return the resolution (precision) of the specified clock *clk_id*.
+   Return the resolution as a floating point number by default, set the
+   optional *format* argument to get another :ref:`timestamp format <timestamp-formats>`.
+
 
    .. versionadded:: 3.3
 
-.. function:: clock_gettime(clk_id)
+.. function:: clock_gettime(clk_id, format=float)
 
    Return the time of the specified clock *clk_id*.
+   Return the time as a floating point number by default, set the optional
+   *format* argument to get another :ref:`timestamp format <timestamp-formats>`.
 
    .. versionadded:: 3.3
 
@@ -431,15 +452,20 @@ The module defines the following functio
    :exc:`TypeError` is raised.
 
 
-.. function:: time()
+.. function:: time(format=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
+   the time as a floating point number by default, set the optional *format*
+   argument to get another :ref:`timestamp format <timestamp-formats>`.
+   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 *format* argument.
+
 
 .. data:: timezone
 
@@ -537,13 +563,15 @@ The module defines the following functio
       ('EET', 'EEST')
 
 
-.. function:: wallclock()
+.. function:: wallclock(format=float)
 
    .. index::
       single: Wallclock
       single: benchmarking
 
    Return the current time in fractions of a second to the system's best ability.
+   Return the time as a floating point number by default, set the optional
+   *format* argument to get another :ref:`timestamp format <timestamp-formats>`.
    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.
diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst
--- a/Doc/whatsnew/3.3.rst
+++ b/Doc/whatsnew/3.3.rst
@@ -270,6 +270,34 @@ new, more precise information::
    '<function C.D.meth at 0x7f46b9fe31e0>'
 
 
+Timestamp formats
+=================
+
+:func:`time.clock`, :func:`time.clock_gettime`, :func:`time.clock_getres`,
+:func:`time.time` and :func:`time.wallclock` now have an optional *format*
+argument to choose the format of the timestamp. See the list of available
+:ref:`timestamp formats <timestamp-formats>`.
+
+Use :class:`decimal.Decimal` to support clock using a nanosecond resolution.
+
+:func:`os.fstat`, :func:`os.fstatat`, :func:`os.lstat` and :func:`os.stat` have
+also a *timestamp* optional argument to choose the timestamp format of
+``st_atime``, ``st_ctime`` and ``st_mtime`` fields.
+
+Example::
+
+    >>> time.time()
+    1328006975.681211
+    >>> time.time(format=int)
+    1328006979
+    >>> time.time(format=decimal.Decimal)
+    Decimal('1328006983.761119')
+    >>> time.time(format=datetime.datetime)
+    datetime.datetime(2012, 1, 31, 11, 49, 49, 409831)
+    >>> print(time.time(format=datetime.timedelta))
+    15370 days, 10:49:52.842116
+
+
 Other Language Changes
 ======================
 
diff --git a/Include/pytime.h b/Include/pytime.h
--- a/Include/pytime.h
+++ b/Include/pytime.h
@@ -12,6 +12,8 @@ functions and constants
 extern "C" {
 #endif
 
+#include "object.h"
+
 #ifdef HAVE_GETTIMEOFDAY
 typedef struct timeval _PyTime_timeval;
 #else
@@ -37,6 +39,21 @@ do { \
     ((tv_end.tv_sec - tv_start.tv_sec) + \
      (tv_end.tv_usec - tv_start.tv_usec) * 0.000001)
 
+typedef struct {
+    time_t seconds;
+    /* floatpart can be zero */
+    size_t floatpart;
+    /* divisor cannot be zero */
+    size_t divisor;
+    /* the 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);
+
+PyAPI_FUNC(PyObject*) _PyTime_AsFormat(_PyTime_t *ts, PyObject *format);
+
 /* Dummy to force linking. */
 PyAPI_FUNC(void) _PyTime_Init(void);
 
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -217,6 +217,30 @@ class StatAttributeTests(unittest.TestCa
             warnings.simplefilter("ignore", DeprecationWarning)
             self.check_stat_attributes(fname)
 
+    def test_stat_timestamp(self):
+        import datetime
+        import decimal
+
+        old_value = os.stat_float_times()
+        try:
+            # test invalid timestamp format
+            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, datetime.datetime, datetime.timedelta):
+                    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
diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py
--- a/Lib/test/test_time.py
+++ b/Lib/test/test_time.py
@@ -1,10 +1,10 @@
 from test import support
+import locale
+import platform
+import sys
+import sysconfig
 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
@@ -331,6 +331,41 @@ class TimeTestCase(unittest.TestCase):
             pass
         self.assertEqual(time.strftime('%Z', tt), tzname)
 
+    def test_format(self):
+        import datetime, decimal
+        calls = [(time.time,)]
+        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 format
+            self.assertRaises(ValueError, func, *args, format="abc")
+            self.assertRaises(ValueError, func, *args, format=decimal.Context)
+
+            # test float
+            timestamp = func(*args, format=float)
+            self.assertIsInstance(timestamp, float)
+
+            # test int
+            timestamp = func(*args, format=int)
+            self.assertIsInstance(timestamp, int)
+
+            # test decimal
+            timestamp = func(*args, format=decimal.Decimal)
+            self.assertIsInstance(timestamp, decimal.Decimal)
+
+            # test datetime
+            timestamp = func(*args, format=datetime.datetime)
+            self.assertIsInstance(timestamp, datetime.datetime)
+            timestamp = func(*args, format=datetime.timedelta)
+            self.assertIsInstance(timestamp, datetime.timedelta)
+
     def test_wallclock(self):
         t1 = time.wallclock()
         t2 = time.wallclock()
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -1708,9 +1708,12 @@ stat_float_times(PyObject* self, PyObjec
 }
 
 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,
+          PyObject *timestamp)
 {
     PyObject *fval,*ival;
+    _PyTime_t ts;
+
 #if SIZEOF_TIME_T > SIZEOF_LONG
     ival = PyLong_FromLongLong((PY_LONG_LONG)sec);
 #else
@@ -1718,9 +1721,15 @@ fill_time(PyObject *v, int index, time_t
 #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;
+        ts.floatpart = nsec;
+        ts.divisor = 1000000000;
+        fval = _PyTime_AsFormat(&ts, timestamp);
+    }
+    else {
         fval = ival;
         Py_INCREF(fval);
     }
@@ -1731,7 +1740,7 @@ fill_time(PyObject *v, int index, time_t
 /* 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;
     PyObject *v = PyStructSequence_New(&StatResultType);
@@ -1776,9 +1785,9 @@ _pystat_fromstructstat(STRUCT_STAT *st)
 #else
     ansec = mnsec = cnsec = 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, timestamp);
+    fill_time(v, 8, st->st_mtime, mnsec, timestamp);
+    fill_time(v, 9, st->st_ctime, cnsec, timestamp);
 
 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
     PyStructSequence_SET_ITEM(v, ST_BLKSIZE_IDX,
@@ -1829,7 +1838,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 *, ...),
@@ -1839,15 +1848,18 @@ posix_do_stat(PyObject *self, PyObject *
               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;
@@ -1858,15 +1870,17 @@ posix_do_stat(PyObject *self, PyObject *
 
         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();
-#endif
-
-    if (!PyArg_ParseTuple(args, format,
-                          PyUnicode_FSConverter, &opath))
+    timestamp = NULL;
+#endif
+
+    if (!PyArg_ParseTupleAndKeywords(args, kw, format, kwlist,
+                                     PyUnicode_FSConverter, &opath,
+                                     &timestamp))
         return NULL;
 #ifdef MS_WINDOWS
     if (win32_warn_bytes_api()) {
@@ -1887,7 +1901,7 @@ posix_do_stat(PyObject *self, PyObject *
 #endif
     }
     else
-        result = _pystat_fromstructstat(&st);
+        result = _pystat_fromstructstat(&st, timestamp);
 
     Py_DECREF(opath);
     return result;
@@ -3362,12 +3376,12 @@ PyDoc_STRVAR(posix_stat__doc__,
 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: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
 }
 
@@ -6367,16 +6381,16 @@ PyDoc_STRVAR(posix_lstat__doc__,
 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 */
 }
@@ -7339,12 +7353,15 @@ PyDoc_STRVAR(posix_fstat__doc__,
 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 */
@@ -7363,7 +7380,7 @@ posix_fstat(PyObject *self, PyObject *ar
 #endif
     }
 
-    return _pystat_fromstructstat(&st);
+    return _pystat_fromstructstat(&st, timestamp);
 }
 
 PyDoc_STRVAR(posix_isatty__doc__,
@@ -9654,15 +9671,18 @@ If path is relative and dirfd is the spe
 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;
-
-    if (!PyArg_ParseTuple(args, "iO&|i:fstatat",
-            &dirfd, PyUnicode_FSConverter, &opath, &flags))
+    PyObject *timestamp = NULL;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iO&|iO:fstatat", kwlist,
+                                     &dirfd, PyUnicode_FSConverter, &opath,
+                                     &flags, &timestamp))
         return NULL;
     path = PyBytes_AsString(opath);
 
@@ -9673,7 +9693,7 @@ posix_fstatat(PyObject *self, PyObject *
     if (res != 0)
         return posix_error();
 
-    return _pystat_fromstructstat(&st);
+    return _pystat_fromstructstat(&st, timestamp);
 }
 #endif
 
@@ -10537,7 +10557,7 @@ static PyMethodDef posix_methods[] = {
 #ifdef HAVE_FDOPENDIR
     {"fdlistdir",       posix_fdlistdir, METH_VARARGS, posix_fdlistdir__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__},
@@ -10556,7 +10576,7 @@ static PyMethodDef posix_methods[] = {
 #endif /* !defined(HAVE_READLINK) && defined(MS_WINDOWS) */
     {"rename",          posix_rename, METH_VARARGS, posix_rename__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__},
@@ -10771,7 +10791,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__},
@@ -10906,7 +10927,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__},
diff --git a/Modules/timemodule.c b/Modules/timemodule.c
--- a/Modules/timemodule.c
+++ b/Modules/timemodule.c
@@ -40,20 +40,26 @@
 #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[] = {"format", NULL};
+    PyObject *format = NULL;
+    _PyTime_t ts;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O:time", kwlist,
+                                     &format))
         return NULL;
-    }
-    return PyFloat_FromDouble(secs);
+
+    _PyTime_get(&ts);
+    return _PyTime_AsFormat(&ts, format);
 }
 
 PyDoc_STRVAR(time_doc,
@@ -72,55 +78,80 @@ Fractions of a second may be present if 
 #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(size_t));
+    ts->floatpart = Py_SAFE_DOWNCAST(processor_time, clock_t, size_t);
+    ts->divisor = 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 *
-time_clock(PyObject *self, PyObject *unused)
+static int
+win32_pyclock(_PyTime_t *ts)
 {
     static LARGE_INTEGER ctrStart;
-    static double divisor = 0.0;
+    static LONGLONG cpu_frequency = 0;
     LARGE_INTEGER now;
-    double diff;
+    LONGLONG dt;
 
-    if (divisor == 0.0) {
+    if (cpu_frequency == 0) {
         LARGE_INTEGER freq;
         QueryPerformanceCounter(&ctrStart);
-        if (!QueryPerformanceFrequency(&freq) || freq.QuadPart == 0) {
+        if (!QueryPerformanceFrequency(&freq) || freq.QuadPart == 0)
+        {
             /* Unlikely to happen - this works on all intel
                machines at least!  Revert to clock() */
-            return pyclock();
+            return pyclock(ts);
         }
-        divisor = (double)freq.QuadPart;
+        cpu_frequency = freq.QuadPart;
     }
     QueryPerformanceCounter(&now);
-    diff = (double)(now.QuadPart - ctrStart.QuadPart);
-    return PyFloat_FromDouble(diff / divisor);
+    dt = now.QuadPart - ctrStart.QuadPart;
+
+    ts->seconds = 0;
+    assert(sizeof(LONGLONG) <= sizeof(size_t));
+    ts->floatpart = Py_SAFE_DOWNCAST(dt, LONGLONG, size_t);
+    ts->divisor = Py_SAFE_DOWNCAST(cpu_frequency, LONGLONG, size_t);
+    return 0;
 }
+#endif
 
-#elif defined(HAVE_CLOCK)
+#ifdef HAVE_PYCLOCK
+static PyObject *
+time_clock(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+    static char *kwlist[] = {"format", NULL};
+    _PyTime_t ts;
+    PyObject *format = NULL;
 
-static PyObject *
-time_clock(PyObject *self, PyObject *unused)
-{
-    return pyclock();
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O:clock", kwlist,
+                                     &format))
+        return NULL;
+
+#if defined(MS_WINDOWS) && !defined(__BORLANDC__)
+    if (win32_pyclock(&ts))
+        return NULL;
+#else
+    if (pyclock(&ts))
+        return NULL;
+#endif
+    return _PyTime_AsFormat(&ts, format);
 }
-#endif /* HAVE_CLOCK */
+#endif
 
 
 #ifdef HAVE_CLOCK
@@ -134,13 +165,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", "format", NULL};
+    PyObject *format = 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, &format))
         return NULL;
 
     ret = clock_gettime((clockid_t)clk_id, &tp);
@@ -148,8 +183,10 @@ time_clock_gettime(PyObject *self, PyObj
         PyErr_SetFromErrno(PyExc_IOError);
         return NULL;
     }
-
-    return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
+    ts.seconds = tp.tv_sec;
+    ts.floatpart = tp.tv_nsec;
+    ts.divisor = 1000000000;
+    return _PyTime_AsFormat(&ts, format);
 }
 
 PyDoc_STRVAR(clock_gettime_doc,
@@ -160,13 +197,17 @@ Return the time of the specified clock c
 
 #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", "format", NULL};
+    PyObject *format = 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, &format))
         return NULL;
 
     ret = clock_getres((clockid_t)clk_id, &tp);
@@ -174,8 +215,10 @@ time_clock_getres(PyObject *self, PyObje
         PyErr_SetFromErrno(PyExc_IOError);
         return NULL;
     }
-
-    return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
+    ts.seconds = tp.tv_sec;
+    ts.floatpart = tp.tv_nsec;
+    ts.divisor = 1000000000;
+    return _PyTime_AsFormat(&ts, format);
 }
 
 PyDoc_STRVAR(clock_getres_doc,
@@ -763,12 +806,14 @@ the local timezone used by methods such 
 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 time_clock(self, NULL);
-#elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
+    return win32_pyclock(ts);
+#else
+
+#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
     static int clk_index = 0;
     clockid_t clk_ids[] = {
 #ifdef CLOCK_MONOTONIC_RAW
@@ -788,18 +833,38 @@ time_wallclock(PyObject *self, PyObject 
         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->floatpart = tp.tv_nsec;
+            ts->divisor = 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[] = {"format", NULL};
+    PyObject *format = NULL;
+    _PyTime_t ts;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O:wallclock", kwlist,
+                                     &format))
+        return NULL;
+    if (pywallclock(&ts))
+        return NULL;
+    return _PyTime_AsFormat(&ts, format);
+}
+
 PyDoc_STRVAR(wallclock_doc,
 "wallclock() -> float\n\
 \n\
@@ -919,15 +984,17 @@ 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},
@@ -944,7 +1011,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 */
 };
 
@@ -1029,15 +1097,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. */
diff --git a/Python/pytime.c b/Python/pytime.c
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -19,8 +19,14 @@ extern int ftime(struct timeb *);
 #endif /* MS_WINDOWS */
 #endif /* HAVE_FTIME */
 
+#define MILLISECONDS       1000
+#define MICROSECONDS    1000000
+#define NANOSECONDS  1000000000
+
+static PyObject* datetime_module = NULL;
+
 void
-_PyTime_gettimeofday(_PyTime_timeval *tp)
+_PyTime_get(_PyTime_t *ts)
 {
     /* There are three ways to get the time:
       (1) gettimeofday() -- resolution in microseconds
@@ -30,27 +36,409 @@ _PyTime_gettimeofday(_PyTime_timeval *tp
       Since on some systems (e.g. SCO ODT 3.0) gettimeofday() may
       fail, so we fall back on ftime() or time().
       Note: clock resolution does not imply clock accuracy! */
+
+#ifdef HAVE_GETTIMEOFDAY
+    struct timeval tv;
+    int err;
+#endif
+#if defined(HAVE_FTIME)
+    struct timeb t;
+#endif
+
 #ifdef HAVE_GETTIMEOFDAY
 #ifdef GETTIMEOFDAY_NO_TZ
-    if (gettimeofday(tp) == 0)
+    err = gettimeofday(&tv);
+#else /* !GETTIMEOFDAY_NO_TZ */
+    err = gettimeofday(&tv, (struct timezone *)NULL);
+#endif /* !GETTIMEOFDAY_NO_TZ */
+    if (err == 0)
+    {
+        ts->seconds = tv.tv_sec;
+        ts->floatpart = tv.tv_usec;
+        ts->divisor = MICROSECONDS;
         return;
-#else /* !GETTIMEOFDAY_NO_TZ */
-    if (gettimeofday(tp, (struct timezone *)NULL) == 0)
-        return;
-#endif /* !GETTIMEOFDAY_NO_TZ */
+    }
 #endif /* !HAVE_GETTIMEOFDAY */
+
 #if defined(HAVE_FTIME)
+    ftime(&t);
+    ts->seconds = t.time;
+    ts->floatpart = t.millitm;
+    ts->divisor = MILLISECONDS;
+#else /* !HAVE_FTIME */
+    ts->seconds = time(NULL);
+    ts->floatpart = 0;
+    ts->divisor = 1;
+#endif /* !HAVE_FTIME */
+}
+
+void
+_PyTime_gettimeofday(_PyTime_timeval *tv)
+{
+    _PyTime_t ts;
+    size_t k;
+
+    _PyTime_get(&ts);
+    tv->tv_sec = ts.seconds;
+    if (ts.floatpart) {
+        if (ts.floatpart > ts.divisor) {
+            tv->tv_sec += ts.floatpart / ts.divisor;
+            ts.floatpart = ts.floatpart % ts.divisor;
+        }
+        if (MICROSECONDS >= ts.divisor) {
+            k = MICROSECONDS / ts.divisor;
+            tv->tv_usec = ts.floatpart * k;
+        }
+        else {
+            k = ts.divisor / MICROSECONDS;
+            tv->tv_usec = ts.floatpart / k;
+        }
+    }
+    else {
+        tv->tv_usec = 0;
+    }
+}
+
+static PyObject*
+PyLong_FromTime_t(time_t value)
+{
+#ifdef SIZEOF_TIME_T <= SIZEOF_LONG
+    return PyLong_FromLong(value);
+#else
+    assert(sizeof(time_t) <= sizeof(PY_LONG_LONG));
+    return PyLong_FromLongLong(value);
+#endif
+}
+
+/* Convert a timestamp to a PyFloat object */
+static PyObject*
+_PyTime_AsFloat(_PyTime_t *ts)
+{
+    double d;
+    d = (double)ts->seconds;
+    d += (double)ts->floatpart / (double)ts->divisor;
+    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_FromSize_t(ts->floatpart / ts->divisor);
+    if (b == NULL)
     {
-        struct timeb t;
-        ftime(&t);
-        tp->tv_sec = t.time;
-        tp->tv_usec = t.millitm * 1000;
+        Py_DECREF(a);
+        return NULL;
     }
-#else /* !HAVE_FTIME */
-    tp->tv_sec = time(NULL);
-    tp->tv_usec = 0;
-#endif /* !HAVE_FTIME */
-    return;
+    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* context = NULL;
+    /* resolution cache (1/divisor), dictionary of int (divisor)=>Decimal */
+    static PyObject* exponents = 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)
+    {
+        /* context = decimal.Context(22, rounding=decimal.ROUND_HALF_EVEN) */
+        PyObject *context_class, *rounding;
+        /* 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_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);
+        Py_DECREF(context_class);
+        Py_DECREF(rounding);
+        if (context == NULL)
+            return NULL;
+    }
+
+    /* t = decimal.Decimal(value, context) */
+    if (ts->seconds) {
+        PyObject *f = PyLong_FromTime_t(ts->seconds);
+        t = PyObject_CallFunction(decimal, "OO", f, context);
+        Py_CLEAR(f);
+    }
+    else {
+        t = PyObject_CallFunction(decimal, "iO", 0, context);
+    }
+    if (t == NULL)
+        return NULL;
+
+    if (ts->floatpart)
+    {
+        /* t += decimal.Decimal(floatpart, ctx) / decimal.Decimal(divisor, ctx) */
+        PyObject *a, *b, *c, *d, *x;
+
+        x = PyLong_FromSize_t(ts->floatpart);
+        if (x == NULL)
+            goto error;
+        a = PyObject_CallFunction(decimal, "OO", x, context);
+        Py_CLEAR(x);
+        if (a == NULL)
+            goto error;
+
+        x = PyLong_FromSize_t(ts->divisor);
+        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 (exponents == NULL) {
+        exponents = PyDict_New();
+        if (exponents == NULL)
+            goto error;
+    }
+
+    key = PyLong_FromSize_t(ts->divisor);
+    if (key == NULL)
+        goto error;
+    exponent = PyDict_GetItem(exponents, key);
+    if (exponent == NULL) {
+        /* exponent = decimal.Decimal(1) / decimal.Decimal(resolution) */
+        PyObject *one, *divisor;
+
+        one = PyObject_CallFunction(decimal, "i", 1);
+        if (one == NULL) {
+            Py_DECREF(key);
+            goto error;
+        }
+
+        divisor = PyObject_CallFunction(decimal, "O", key);
+        if (divisor == NULL) {
+            Py_DECREF(key);
+            Py_DECREF(one);
+            goto error;
+        }
+
+        exponent = _PyObject_CallMethodId(one, &PyId___truediv__, "OO",
+                                          divisor, context);
+        Py_DECREF(one);
+        Py_DECREF(divisor);
+        if (exponent == NULL) {
+            Py_DECREF(key);
+            goto error;
+        }
+
+        if (PyDict_SetItem(exponents, 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;
+}
+
+/* Convert a timestamp to a datetime.datetime object */
+PyObject*
+_PyTime_AsDatetime(_PyTime_t *ts)
+{
+    static PyObject* datetime = NULL;
+    _Py_IDENTIFIER(fromtimestamp);
+    PyObject *decimal, *result;
+
+    if (!datetime_module) {
+        datetime_module = PyImport_ImportModuleNoBlock("datetime");
+        if (datetime_module == NULL)
+            return NULL;
+    }
+
+    if (!datetime) {
+        datetime = PyObject_GetAttrString(datetime_module, "datetime");
+        if (datetime == NULL)
+            return NULL;
+    }
+
+    decimal = _PyTime_AsDecimal(ts);
+    if (decimal == NULL)
+        return NULL;
+    result = _PyObject_CallMethodId(datetime, &PyId_fromtimestamp,
+                                    "O", decimal);
+    Py_DECREF(decimal);
+    return result;
+}
+
+/* Convert a timestamp to a datetime.timedelta object */
+PyObject*
+_PyTime_AsTimedelta(_PyTime_t *ts)
+{
+    static PyObject* timedelta = NULL;
+    unsigned long microseconds;
+    PyObject *seconds_obj, *microseconds_obj, *result;
+
+    if (!datetime_module) {
+        datetime_module = PyImport_ImportModuleNoBlock("datetime");
+        if (datetime_module == NULL)
+            return NULL;
+    }
+
+    if (!timedelta) {
+        timedelta = PyObject_GetAttrString(datetime_module, "timedelta");
+        if (timedelta == NULL)
+            return NULL;
+    }
+
+    seconds_obj = PyLong_FromTime_t(ts->seconds);
+    if (seconds_obj == NULL)
+        return NULL;
+    if (ts->floatpart) {
+        size_t k;
+        if (ts->floatpart > ts->divisor) {
+            PyObject *b, *c;
+            b = PyLong_FromSize_t(ts->floatpart / ts->divisor);
+            ts->floatpart = ts->floatpart % ts->divisor;
+            if (b == NULL) {
+                Py_DECREF(seconds_obj);
+                return NULL;
+            }
+            c = PyNumber_Add(seconds_obj, b);
+            Py_DECREF(seconds_obj);
+            Py_DECREF(b);
+            seconds_obj = c;
+        }
+        if (MICROSECONDS >= ts->divisor) {
+            k = MICROSECONDS / ts->divisor;
+            microseconds = Py_SAFE_DOWNCAST(ts->floatpart * k,
+                                            size_t, unsigned long);
+        }
+        else {
+            k = ts->divisor / MICROSECONDS;
+            microseconds = Py_SAFE_DOWNCAST(ts->floatpart / k,
+                                            size_t, unsigned long);
+        }
+    }
+    else {
+        microseconds = 0;
+    }
+    microseconds_obj = PyLong_FromSize_t(microseconds);
+    if (microseconds_obj == NULL) {
+        Py_DECREF(seconds_obj);
+        return NULL;
+    }
+    result = PyObject_CallFunction(timedelta, "iOO",
+                                   0, seconds_obj, microseconds_obj);
+    Py_DECREF(seconds_obj);
+    Py_DECREF(microseconds_obj);
+    return result;
+}
+
+/* Convert a timestamp to the specified format.
+
+   The result type depends on the requested format.
+
+   Raise a ValueError if the format is unknown. */
+PyObject*
+_PyTime_AsFormat(_PyTime_t *ts, PyObject *format)
+{
+    assert(ts->divisor != 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);
+            if (PyUnicode_CompareWithASCIIString(module, "datetime") == 0)
+            {
+                if (PyUnicode_CompareWithASCIIString(name, "datetime") == 0)
+                    return _PyTime_AsDatetime(ts);
+                if (PyUnicode_CompareWithASCIIString(name, "timedelta") == 0)
+                    return _PyTime_AsTimedelta(ts);
+            }
+        }
+        else
+            PyErr_Clear();
+    }
+
+    PyErr_Format(PyExc_ValueError, "Unknown timestamp format: %R", format);
+    return NULL;
 }
 
 void


More information about the Python-bugs-list mailing list