Issue #14428, #14397: Implement the PEP 418

 * Rename time.steady() to time.monotonic()
 * On Windows, time.monotonic() uses GetTickCount/GetTickCount64() instead of
   QueryPerformanceCounter()
 * time.monotonic() uses CLOCK_HIGHRES if available
 * Add time.get_clock_info(), time.perf_counter() and time.process_time()
   functions
diff --git a/Doc/library/time.rst b/Doc/library/time.rst
index 70ef114..56ca865 100644
--- a/Doc/library/time.rst
+++ b/Doc/library/time.rst
@@ -155,6 +155,30 @@
    .. versionadded:: 3.3
 
 
+.. class:: clock_info
+
+   Clock information object created by :func:`get_clock_info`.
+
+   .. attribute:: implementation
+
+      name of the underlying C function used to get the clock value
+
+   .. attribute::  is_monotonic
+
+      ``True`` if the clock cannot go backward, ``False`` otherwise
+
+   .. attribute:: is_adjusted
+
+      ``True`` if the clock can be adjusted (e.g. by a NTP daemon),
+      ``False`` otherwise
+
+   .. attribute:: resolution
+
+      Resolution of the clock in seconds (:class:`float`)
+
+   .. versionadded:: 3.3
+
+
 .. function:: clock_settime(clk_id, time)
 
    Set the time of the specified clock *clk_id*.
@@ -236,6 +260,22 @@
    Nonzero if a DST timezone is defined.
 
 
+.. function:: get_clock_info(name)
+
+   Get information on the specified clock as a :class:`clock_info` object.
+
+   Supported clock names:
+
+
+    * ``'clock'``: :func:`time.clock`
+    * ``'monotonic'``: :func:`time.monotonic`
+    * ``'perf_counter'``: :func:`time.perf_counter`
+    * ``'process_time'``: :func:`time.process_time`
+    * ``'time'``: :func:`time.time`
+
+   .. versionadded:: 3.3
+
+
 .. function:: gmtime([secs])
 
    Convert a time expressed in seconds since the epoch to a :class:`struct_time` in
@@ -265,20 +305,43 @@
    The earliest date for which it can generate a time is platform-dependent.
 
 
-.. function:: steady(strict=False)
+.. function:: monotonic()
 
-   .. index::
-      single: benchmarking
+   Monotonic clock, i.e. cannot go backward.  It is not affected by system
+   clock updates.  The reference point of the returned value is undefined, so
+   that only the difference between the results of consecutive calls is valid
+   and is a number of seconds.
 
-   Return the current time as a floating point number expressed in seconds.
-   This clock advances at a steady rate relative to real time and it may not be
-   adjusted. The reference point of the returned value is undefined so only the
-   difference of consecutive calls is valid.
+   On Windows versions older than Vista, :func:`monotonic` detects
+   :c:func:`GetTickCount` integer overflow (32 bits, roll-over after 49.7
+   days).  It increases an internal epoch (reference time by) 2\ :sup:`32` each
+   time that an overflow is detected.  The epoch is stored in the process-local
+   state and so the value of :func:`monotonic` may be different in two Python
+   processes running for more than 49 days. On more recent versions of Windows
+   and on other operating systems, :func:`monotonic` is system-wide.
 
-   If available, a monotonic clock is used. By default,
-   the function falls back to another clock if the monotonic clock failed or is
-   not available. If *strict* is True, raise an :exc:`OSError` on error or
-   :exc:`NotImplementedError` if no monotonic clock is available.
+   Availability: Windows, Mac OS X, Linux, FreeBSD, OpenBSD, Solaris.
+
+   .. versionadded:: 3.3
+
+
+.. function:: perf_counter()
+
+   Performance counter with the highest available resolution to measure a short
+   duration.  It does include time elapsed during sleep and is system-wide.
+   The reference point of the returned value is undefined, so that only the
+   difference between the results of consecutive calls is valid and is a number
+   of seconds.
+
+   .. versionadded:: 3.3
+
+
+.. function:: process_time()
+
+   Sum of the system and user CPU time of the current process. It does not
+   include time elapsed during sleep. It is process-wide by definition.  The
+   reference point of the returned value is undefined, so that only the
+   difference between the results of consecutive calls is valid.
 
    .. versionadded:: 3.3
 
diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst
index 495243f..3d63127 100644
--- a/Doc/whatsnew/3.3.rst
+++ b/Doc/whatsnew/3.3.rst
@@ -1059,13 +1059,21 @@
 time
 ----
 
-The :mod:`time` module has new functions:
+The :pep:`418` added new functions to the :mod:`time` module:
 
-* :func:`~time.clock_getres` and :func:`~time.clock_gettime` functions and
-  ``CLOCK_xxx`` constants.
-* :func:`~time.steady`.
+* :func:`~time.get_clock_info`: Get information on a clock.
+* :func:`~time.monotonic`: Monotonic clock (cannot go backward), not affected
+  by system clock updates.
+* :func:`~time.perf_counter`: Performance counter with the highest available
+  resolution to measure a short duration.
+* :func:`~time.process_time`: Sum of the system and user CPU time of the
+  current process.
 
-(Contributed by Victor Stinner in :issue:`10278`)
+Other new functions:
+
+* :func:`~time.clock_getres`, :func:`~time.clock_gettime` and
+  :func:`~time.clock_settime` functions with ``CLOCK_xxx`` constants.
+  (Contributed by Victor Stinner in :issue:`10278`)
 
 
 types
diff --git a/Include/pytime.h b/Include/pytime.h
index 221279b..a0cedb2 100644
--- a/Include/pytime.h
+++ b/Include/pytime.h
@@ -22,11 +22,25 @@
 } _PyTime_timeval;
 #endif
 
+/* Structure used by time.get_clock_info() */
+typedef struct {
+    const char *implementation;
+    int is_monotonic;
+    int is_adjusted;
+    double resolution;
+} _Py_clock_info_t;
+
 /* Similar to POSIX gettimeofday but cannot fail.  If system gettimeofday
  * fails or is not available, fall back to lower resolution clocks.
  */
 PyAPI_FUNC(void) _PyTime_gettimeofday(_PyTime_timeval *tp);
 
+/* Similar to _PyTime_gettimeofday() but retrieve also information on the
+ * clock used to get the current time. */
+PyAPI_FUNC(void) _PyTime_gettimeofday_info(
+    _PyTime_timeval *tp,
+    _Py_clock_info_t *info);
+
 #define _PyTime_ADD_SECONDS(tv, interval) \
 do { \
     tv.tv_usec += (long) (((long) interval - interval) * 1000000); \
diff --git a/Lib/queue.py b/Lib/queue.py
index 1dc72c4..c3296fe 100644
--- a/Lib/queue.py
+++ b/Lib/queue.py
@@ -6,7 +6,10 @@
     import dummy_threading as threading
 from collections import deque
 from heapq import heappush, heappop
-from time import steady as time
+try:
+    from time import monotonic as time
+except ImportError:
+    from time import time
 
 __all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue']
 
diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py
index fb2489c..f00660b 100644
--- a/Lib/test/test_time.py
+++ b/Lib/test/test_time.py
@@ -5,6 +5,10 @@
 import sysconfig
 import sys
 import platform
+try:
+    import threading
+except ImportError:
+    threading = None
 
 # Max year is only limited by the size of C int.
 SIZEOF_INT = sysconfig.get_config_var('SIZEOF_INT') or 4
@@ -23,9 +27,20 @@
         time.timezone
         time.tzname
 
+    def test_time(self):
+        time.time()
+        info = time.get_clock_info('time')
+        self.assertEqual(info.is_monotonic, False)
+        if sys.platform != 'win32':
+            self.assertEqual(info.is_adjusted, True)
+
     def test_clock(self):
         time.clock()
 
+        info = time.get_clock_info('clock')
+        self.assertEqual(info.is_monotonic, True)
+        self.assertEqual(info.is_adjusted, False)
+
     @unittest.skipUnless(hasattr(time, 'clock_gettime'),
                          'need time.clock_gettime()')
     def test_clock_realtime(self):
@@ -56,7 +71,9 @@
         except PermissionError:
             pass
 
-        self.assertRaises(OSError, time.clock_settime, time.CLOCK_MONOTONIC, 0)
+        if hasattr(time, 'CLOCK_MONOTONIC'):
+            self.assertRaises(OSError,
+                              time.clock_settime, time.CLOCK_MONOTONIC, 0)
 
     def test_conversions(self):
         self.assertEqual(time.ctime(self.t),
@@ -342,23 +359,69 @@
             pass
         self.assertEqual(time.strftime('%Z', tt), tzname)
 
-    def test_steady(self):
-        t1 = time.steady()
+    @unittest.skipUnless(hasattr(time, 'monotonic'),
+                         'need time.monotonic')
+    def test_monotonic(self):
+        t1 = time.monotonic()
         time.sleep(0.1)
-        t2 = time.steady()
+        t2 = time.monotonic()
         dt = t2 - t1
-        # may fail if the system clock was changed
         self.assertGreater(t2, t1)
         self.assertAlmostEqual(dt, 0.1, delta=0.2)
 
-    def test_steady_strict(self):
+        info = time.get_clock_info('monotonic')
+        self.assertEqual(info.is_monotonic, True)
+        if sys.platform == 'linux':
+            self.assertEqual(info.is_adjusted, True)
+        else:
+            self.assertEqual(info.is_adjusted, False)
+
+    def test_perf_counter(self):
+        time.perf_counter()
+
+    def test_process_time(self):
+        start = time.process_time()
+        time.sleep(0.1)
+        stop = time.process_time()
+        self.assertLess(stop - start, 0.01)
+
+        info = time.get_clock_info('process_time')
+        self.assertEqual(info.is_monotonic, True)
+        self.assertEqual(info.is_adjusted, False)
+
+    @unittest.skipUnless(threading,
+                         'need threading')
+    def test_process_time_threads(self):
+        class BusyThread(threading.Thread):
+            def run(self):
+                while not self.stop:
+                    pass
+
+        thread = BusyThread()
+        thread.stop = False
+        t1 = time.process_time()
+        thread.start()
+        time.sleep(0.2)
+        t2 = time.process_time()
+        thread.stop = True
+        thread.join()
+        self.assertGreater(t2 - t1, 0.1)
+
+    @unittest.skipUnless(hasattr(time, 'monotonic'),
+                         'need time.monotonic')
+    @unittest.skipUnless(hasattr(time, 'clock_settime'),
+                         'need time.clock_settime')
+    def test_monotonic_settime(self):
+        t1 = time.monotonic()
+        realtime = time.clock_gettime(time.CLOCK_REALTIME)
+        # jump backward with an offset of 1 hour
         try:
-            t1 = time.steady(strict=True)
-        except OSError as err:
-            self.skipTest("the monotonic clock failed: %s" % err)
-        except NotImplementedError:
-            self.skipTest("no monotonic clock available")
-        t2 = time.steady(strict=True)
+            time.clock_settime(time.CLOCK_REALTIME, realtime - 3600)
+        except PermissionError as err:
+            self.skipTest(err)
+        t2 = time.monotonic()
+        time.clock_settime(time.CLOCK_REALTIME, realtime)
+        # monotonic must not be affected by system clock updates
         self.assertGreaterEqual(t2, t1)
 
     def test_localtime_failure(self):
@@ -378,6 +441,26 @@
         self.assertRaises(OSError, time.localtime, invalid_time_t)
         self.assertRaises(OSError, time.ctime, invalid_time_t)
 
+    def test_get_clock_info(self):
+        clocks = ['clock', 'perf_counter', 'process_time', 'time']
+        if hasattr(time, 'monotonic'):
+            clocks.append('monotonic')
+
+        for name in clocks:
+            info = time.get_clock_info(name)
+            #self.assertIsInstance(info, dict)
+            self.assertIsInstance(info.implementation, str)
+            self.assertNotEqual(info.implementation, '')
+            self.assertIsInstance(info.is_monotonic, bool)
+            self.assertIsInstance(info.resolution, float)
+            # 0.0 < resolution <= 1.0
+            self.assertGreater(info.resolution, 0.0)
+            self.assertLessEqual(info.resolution, 1.0)
+            self.assertIsInstance(info.is_adjusted, bool)
+
+        self.assertRaises(ValueError, time.get_clock_info, 'xxx')
+
+
 class TestLocale(unittest.TestCase):
     def setUp(self):
         self.oldloc = locale.setlocale(locale.LC_ALL)
diff --git a/Lib/threading.py b/Lib/threading.py
index 8c2cee9..6c34d49 100644
--- a/Lib/threading.py
+++ b/Lib/threading.py
@@ -3,7 +3,11 @@
 import sys as _sys
 import _thread
 
-from time import steady as _time, sleep as _sleep
+from time import sleep as _sleep
+try:
+    from time import monotonic as _time
+except ImportError:
+    from time import time as _time
 from traceback import format_exc as _format_exc
 from _weakrefset import WeakSet
 
diff --git a/Misc/NEWS b/Misc/NEWS
index 98081c7..995ec9e 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -81,6 +81,10 @@
 Library
 -------
 
+- Issue #14428: Implement the PEP 418. Add time.get_clock_info(),
+  time.perf_counter() and time.process_time() functions, and rename
+  time.steady() to time.monotonic().
+
 - Issue #14646: importlib.util.module_for_loader() now sets __loader__ and
   __package__ (when possible).
 
diff --git a/Modules/timemodule.c b/Modules/timemodule.c
index 771db83..1e843b9 100644
--- a/Modules/timemodule.c
+++ b/Modules/timemodule.c
@@ -4,9 +4,17 @@
 
 #include <ctype.h>
 
+#ifdef HAVE_SYS_TIMES_H
+#include <sys/times.h>
+#endif
+
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
-#endif /* HAVE_SYS_TYPES_H */
+#endif
+
+#if defined(HAVE_SYS_RESOURCE_H)
+#include <sys/resource.h>
+#endif
 
 #ifdef QUICKWIN
 #include <io.h>
@@ -45,12 +53,16 @@
 
 /* Forward declarations */
 static int floatsleep(double);
-static PyObject* floattime(void);
+static PyObject* floattime(_Py_clock_info_t *info);
+
+#ifdef MS_WINDOWS
+static OSVERSIONINFOEX winver;
+#endif
 
 static PyObject *
 time_time(PyObject *self, PyObject *unused)
 {
-    return floattime();
+    return floattime(NULL);
 }
 
 PyDoc_STRVAR(time_doc,
@@ -70,7 +82,7 @@
 #endif
 
 static PyObject *
-pyclock(void)
+floatclock(_Py_clock_info_t *info)
 {
     clock_t value;
     value = clock();
@@ -80,15 +92,22 @@
                 "or its value cannot be represented");
         return NULL;
     }
+    if (info) {
+        info->implementation = "clock()";
+        info->resolution = 1.0 / (double)CLOCKS_PER_SEC;
+        info->is_monotonic = 1;
+        info->is_adjusted = 0;
+    }
     return PyFloat_FromDouble((double)value / CLOCKS_PER_SEC);
 }
 #endif /* HAVE_CLOCK */
 
 #if defined(MS_WINDOWS) && !defined(__BORLANDC__)
+#define WIN32_PERF_COUNTER
 /* 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
+win_perf_counter(_Py_clock_info_t *info, PyObject **result)
 {
     static LONGLONG cpu_frequency = 0;
     static LONGLONG ctrStart;
@@ -102,28 +121,41 @@
         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);
+            *result = NULL;
+            return -1;
         }
         cpu_frequency = freq.QuadPart;
     }
     QueryPerformanceCounter(&now);
     diff = (double)(now.QuadPart - ctrStart);
-    return PyFloat_FromDouble(diff / (double)cpu_frequency);
+    if (info) {
+        info->implementation = "QueryPerformanceCounter()";
+        info->resolution = 1.0 / (double)cpu_frequency;
+        info->is_monotonic = 1;
+        info->is_adjusted = 0;
+    }
+    *result = PyFloat_FromDouble(diff / (double)cpu_frequency);
+    return 0;
 }
 #endif
 
-#if (defined(MS_WINDOWS) && !defined(__BORLANDC__)) || defined(HAVE_CLOCK)
+#if defined(WIN32_PERF_COUNTER) || defined(HAVE_CLOCK)
+#define PYCLOCK
+static PyObject*
+pyclock(_Py_clock_info_t *info)
+{
+#ifdef WIN32_PERF_COUNTER
+    PyObject *res;
+    if (win_perf_counter(info, &res) == 0)
+        return res;
+#endif
+    return floatclock(info);
+}
+
 static PyObject *
 time_clock(PyObject *self, PyObject *unused)
 {
-#if defined(MS_WINDOWS) && !defined(__BORLANDC__)
-    return win32_clock(1);
-#else
-    return pyclock();
-#endif
+    return pyclock(NULL);
 }
 
 PyDoc_STRVAR(clock_doc,
@@ -150,7 +182,6 @@
         PyErr_SetFromErrno(PyExc_IOError);
         return NULL;
     }
-
     return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
 }
 
@@ -787,11 +818,74 @@
 should not be relied on.");
 #endif /* HAVE_WORKING_TZSET */
 
+#if defined(MS_WINDOWS) || defined(__APPLE__) \
+    || (defined(HAVE_CLOCK_GETTIME) \
+        && (defined(CLOCK_HIGHRES) || defined(CLOCK_MONOTONIC)))
+#define PYMONOTONIC
+#endif
+
+#ifdef PYMONOTONIC
 static PyObject*
-steady_clock(int strict)
+pymonotonic(_Py_clock_info_t *info)
 {
-#if defined(MS_WINDOWS) && !defined(__BORLANDC__)
-    return win32_clock(!strict);
+#if defined(MS_WINDOWS)
+    static ULONGLONG (*GetTickCount64) (void) = NULL;
+    static ULONGLONG (CALLBACK *Py_GetTickCount64)(void);
+    static int has_getickcount64 = -1;
+    double result;
+
+    if (has_getickcount64 == -1) {
+        /* GetTickCount64() was added to Windows Vista */
+        if (winver.dwMajorVersion >= 6) {
+            HINSTANCE hKernel32;
+            hKernel32 = GetModuleHandleW(L"KERNEL32");
+            *(FARPROC*)&Py_GetTickCount64 = GetProcAddress(hKernel32,
+                                                           "GetTickCount64");
+            has_getickcount64 = (Py_GetTickCount64 != NULL);
+        }
+        else
+            has_getickcount64 = 0;
+    }
+
+    if (has_getickcount64) {
+        ULONGLONG ticks;
+        ticks = Py_GetTickCount64();
+        result = (double)ticks * 1e-3;
+    }
+    else {
+        static DWORD last_ticks = 0;
+        static DWORD n_overflow = 0;
+        DWORD ticks;
+
+        ticks = GetTickCount();
+        if (ticks < last_ticks)
+            n_overflow++;
+        last_ticks = ticks;
+
+        result = ldexp(n_overflow, 32);
+        result += ticks;
+        result *= 1e-3;
+    }
+
+    if (info) {
+        DWORD timeAdjustment, timeIncrement;
+        BOOL isTimeAdjustmentDisabled, ok;
+        if (has_getickcount64)
+            info->implementation = "GetTickCount64()";
+        else
+            info->implementation = "GetTickCount()";
+        info->is_monotonic = 1;
+        ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
+                                     &isTimeAdjustmentDisabled);
+        if (!ok) {
+            PyErr_SetFromWindowsErr(0);
+            return NULL;
+        }
+        info->resolution = timeIncrement * 1e-7;
+        info->is_adjusted = 0;
+    }
+    return PyFloat_FromDouble(result);
+
 #elif defined(__APPLE__)
     static mach_timebase_info_data_t timebase;
     uint64_t time;
@@ -805,88 +899,338 @@
 
     time = mach_absolute_time();
     secs = (double)time * timebase.numer / timebase.denom * 1e-9;
-
+    if (info) {
+        info->implementation = "mach_absolute_time()";
+        info->resolution = (double)timebase.numer / timebase.denom * 1e-9;
+        info->is_monotonic = 1;
+        info->is_adjusted = 0;
+    }
     return PyFloat_FromDouble(secs);
-#elif defined(HAVE_CLOCK_GETTIME)
-    static int steady_clk_index = 0;
-    static int monotonic_clk_index = 0;
-    int *clk_index;
-    clockid_t steady_clk_ids[] = {
-#ifdef CLOCK_MONOTONIC_RAW
-        CLOCK_MONOTONIC_RAW,
-#endif
-        CLOCK_MONOTONIC,
-        CLOCK_REALTIME
-    };
-    clockid_t monotonic_clk_ids[] = {
-#ifdef CLOCK_MONOTONIC_RAW
-        CLOCK_MONOTONIC_RAW,
-#endif
-        CLOCK_MONOTONIC
-    };
-    clockid_t *clk_ids;
-    int clk_ids_len;
-    int ret;
+
+#elif defined(HAVE_CLOCK_GETTIME) && (defined(CLOCK_HIGHRES) || defined(CLOCK_MONOTONIC))
     struct timespec tp;
+#ifdef CLOCK_HIGHRES
+    const clockid_t clk_id = CLOCK_HIGHRES;
+    const char *function = "clock_gettime(CLOCK_HIGHRES)";
+#else
+    const clockid_t clk_id = CLOCK_MONOTONIC;
+    const char *function = "clock_gettime(CLOCK_MONOTONIC)";
+#endif
 
-    if (strict) {
-        clk_index = &monotonic_clk_index;
-        clk_ids = monotonic_clk_ids;
-        clk_ids_len = Py_ARRAY_LENGTH(monotonic_clk_ids);
-    }
-    else {
-        clk_index = &steady_clk_index;
-        clk_ids = steady_clk_ids;
-        clk_ids_len = Py_ARRAY_LENGTH(steady_clk_ids);
-    }
-
-    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);
-
-        (*clk_index)++;
-        if (clk_ids_len <= *clk_index)
-            (*clk_index) = -1;
-    }
-    if (strict) {
+    if (clock_gettime(clk_id, &tp) != 0) {
         PyErr_SetFromErrno(PyExc_OSError);
         return NULL;
     }
-    return floattime();
+
+    if (info) {
+        struct timespec res;
+        info->is_monotonic = 1;
+        info->implementation = function;
+#if (defined(linux) || defined(__linux) || defined(__linux__)) \
+    && !defined(CLOCK_HIGHRES)
+        /* CLOCK_MONOTONIC is adjusted on Linux */
+        info->is_adjusted = 1;
 #else
-    if (strict) {
-        PyErr_SetString(PyExc_NotImplementedError,
-                        "no steady clock available on your platform");
-        return NULL;
+        info->is_adjusted = 0;
+#endif
+        if (clock_getres(clk_id, &res) == 0)
+            info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
+        else
+            info->resolution = 1e-9;
     }
-    return floattime();
+    return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
 #endif
 }
 
 static PyObject *
-time_steady(PyObject *self, PyObject *args, PyObject *kwargs)
+time_monotonic(PyObject *self, PyObject *unused)
 {
-    static char *kwlist[] = {"strict", NULL};
-    int strict = 0;
-
-    if (!PyArg_ParseTupleAndKeywords(
-            args, kwargs, "|i:steady", kwlist,
-            &strict))
-        return NULL;
-
-    return steady_clock(strict);
+    return pymonotonic(NULL);
 }
 
-PyDoc_STRVAR(steady_doc,
-"steady(strict=False) -> float\n\
+PyDoc_STRVAR(monotonic_doc,
+"monotonic() -> float\n\
 \n\
-Return the current time as a floating point number expressed in seconds.\n\
-This clock advances at a steady rate relative to real time and it may not\n\
-be adjusted. The reference point of the returned value is undefined so only\n\
-the difference of consecutive calls is valid.");
+Monotonic clock, cannot go backward.");
+#endif   /* PYMONOTONIC */
 
+static PyObject*
+perf_counter(_Py_clock_info_t *info)
+{
+#if defined(WIN32_PERF_COUNTER) || defined(PYMONOTONIC)
+    PyObject *res;
+#endif
+#if defined(WIN32_PERF_COUNTER)
+    static int use_perf_counter = 1;
+#endif
+#ifdef PYMONOTONIC
+    static int use_monotonic = 1;
+#endif
+
+#ifdef WIN32_PERF_COUNTER
+    if (use_perf_counter) {
+        if (win_perf_counter(info, &res) == 0)
+            return res;
+        use_perf_counter = 0;
+    }
+#endif
+
+#ifdef PYMONOTONIC
+    if (use_monotonic) {
+        res = pymonotonic(info);
+        if (res != NULL)
+            return res;
+        use_monotonic = 0;
+        PyErr_Clear();
+    }
+#endif
+
+    return floattime(info);
+}
+
+static PyObject *
+time_perf_counter(PyObject *self, PyObject *unused)
+{
+    return perf_counter(NULL);
+}
+
+PyDoc_STRVAR(perf_counter_doc,
+"perf_counter() -> float\n\
+\n\
+Performance counter for benchmarking.");
+
+static PyObject*
+py_process_time(_Py_clock_info_t *info)
+{
+#if defined(MS_WINDOWS)
+    HANDLE process;
+    FILETIME creation_time, exit_time, kernel_time, user_time;
+    ULARGE_INTEGER large;
+    double total;
+    BOOL ok;
+
+    process = GetCurrentProcess();
+    ok = GetProcessTimes(process, &creation_time, &exit_time, &kernel_time, &user_time);
+    if (!ok)
+        return PyErr_SetFromWindowsErr(0);
+
+    large.u.LowPart = kernel_time.dwLowDateTime;
+    large.u.HighPart = kernel_time.dwHighDateTime;
+    total = (double)large.QuadPart;
+    large.u.LowPart = user_time.dwLowDateTime;
+    large.u.HighPart = user_time.dwHighDateTime;
+    total += (double)large.QuadPart;
+    if (info) {
+        info->implementation = "GetProcessTimes()";
+        info->resolution = 1e-7;
+        info->is_monotonic = 1;
+        info->is_adjusted = 0;
+    }
+    return PyFloat_FromDouble(total * 1e-7);
+#else
+
+#if defined(HAVE_SYS_RESOURCE_H)
+    struct rusage ru;
+#endif
+#ifdef HAVE_TIMES
+    struct tms t;
+    static long ticks_per_second = -1;
+#endif
+
+#if defined(HAVE_CLOCK_GETTIME) \
+    && (defined(CLOCK_PROCESS_CPUTIME_ID) || defined(CLOCK_PROF))
+    struct timespec tp;
+#ifdef CLOCK_PROF
+    const clockid_t clk_id = CLOCK_PROF;
+    const char *function = "clock_gettime(CLOCK_PROF)";
+#else
+    const clockid_t clk_id = CLOCK_PROCESS_CPUTIME_ID;
+    const char *function = "clock_gettime(CLOCK_PROCESS_CPUTIME_ID)";
+#endif
+
+    if (clock_gettime(clk_id, &tp) == 0) {
+        if (info) {
+            struct timespec res;
+            info->implementation = function;
+            info->is_monotonic = 1;
+            info->is_adjusted = 0;
+            if (clock_getres(clk_id, &res) == 0)
+                info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
+            else
+                info->resolution = 1e-9;
+        }
+        return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
+    }
+#endif
+
+#if defined(HAVE_SYS_RESOURCE_H)
+    if (getrusage(RUSAGE_SELF, &ru) == 0) {
+        double total;
+        total = ru.ru_utime.tv_sec + ru.ru_utime.tv_usec * 1e-6;
+        total += ru.ru_stime.tv_sec + ru.ru_stime.tv_usec * 1e-6;
+        if (info) {
+            info->implementation = "getrusage(RUSAGE_SELF)";
+            info->is_monotonic = 1;
+            info->is_adjusted = 0;
+            info->resolution = 1e-6;
+        }
+        return PyFloat_FromDouble(total);
+    }
+#endif
+
+#ifdef HAVE_TIMES
+    if (times(&t) != (clock_t)-1) {
+        double total;
+
+        if (ticks_per_second == -1) {
+#if defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK)
+            ticks_per_second = sysconf(_SC_CLK_TCK);
+            if (ticks_per_second < 1)
+                ticks_per_second = -1;
+#elif defined(HZ)
+            ticks_per_second = HZ;
+#else
+            ticks_per_second = 60; /* magic fallback value; may be bogus */
+#endif
+        }
+
+        if (ticks_per_second != -1) {
+            total = (double)t.tms_utime / ticks_per_second;
+            total += (double)t.tms_stime / ticks_per_second;
+            if (info) {
+                info->implementation = "times()";
+                info->is_monotonic = 1;
+                info->is_adjusted = 0;
+                info->resolution = 1.0 / ticks_per_second;
+            }
+            return PyFloat_FromDouble(total);
+        }
+    }
+#endif
+
+    return floatclock(info);
+#endif
+}
+
+static PyObject *
+time_process_time(PyObject *self, PyObject *unused)
+{
+    return py_process_time(NULL);
+}
+
+PyDoc_STRVAR(process_time_doc,
+"process_time() -> float\n\
+\n\
+Process time for profiling: sum of the kernel and user-space CPU time.");
+
+
+static PyTypeObject ClockInfoType;
+
+PyDoc_STRVAR(ClockInfo_docstring,
+    "Clock information");
+
+static PyStructSequence_Field ClockInfo_fields[] = {
+    {"implementation", "name of the underlying C function "
+                       "used to get the clock value"},
+    {"is_monotonic", "True if the clock cannot go backward, False otherwise"},
+    {"is_adjusted", "True if the clock can be adjusted "
+                    "(e.g. by a NTP daemon), False otherwise"},
+    {"resolution", "resolution of the clock in seconds"},
+    {NULL, NULL}
+};
+
+static PyStructSequence_Desc ClockInfo_desc = {
+    "time.clock_info",
+    ClockInfo_docstring,
+    ClockInfo_fields,
+    4,
+};
+
+static PyObject *
+time_get_clock_info(PyObject *self, PyObject *args)
+{
+    char *name;
+    PyObject *obj;
+    _Py_clock_info_t info;
+    PyObject *result;
+
+    if (!PyArg_ParseTuple(args, "s:get_clock_info", &name))
+        return NULL;
+
+#ifdef Py_DEBUG
+    info.implementation = NULL;
+    info.is_monotonic = -1;
+    info.is_adjusted = -1;
+    info.resolution = -1.0;
+#else
+    info.implementation = "";
+    info.is_monotonic = 0;
+    info.is_adjusted = 0;
+    info.resolution = 1.0;
+#endif
+
+    if (strcmp(name, "time") == 0)
+        obj = floattime(&info);
+#ifdef PYCLOCK
+    else if (strcmp(name, "clock") == 0)
+        obj = pyclock(&info);
+#endif
+#ifdef PYMONOTONIC
+    else if (strcmp(name, "monotonic") == 0)
+        obj = pymonotonic(&info);
+#endif
+    else if (strcmp(name, "perf_counter") == 0)
+        obj = perf_counter(&info);
+    else if (strcmp(name, "process_time") == 0)
+        obj = py_process_time(&info);
+    else {
+        PyErr_SetString(PyExc_ValueError, "unknown clock");
+        return NULL;
+    }
+    if (obj == NULL)
+        return NULL;
+    Py_DECREF(obj);
+
+    result = PyStructSequence_New(&ClockInfoType);
+    if (result == NULL)
+        return NULL;
+
+    assert(info.implementation != NULL);
+    obj = PyUnicode_FromString(info.implementation);
+    if (obj == NULL)
+        goto error;
+    PyStructSequence_SET_ITEM(result, 0, obj);
+
+    assert(info.is_monotonic != -1);
+    obj = PyBool_FromLong(info.is_monotonic);
+    if (obj == NULL)
+        goto error;
+    PyStructSequence_SET_ITEM(result, 1, obj);
+
+    assert(info.is_adjusted != -1);
+    obj = PyBool_FromLong(info.is_adjusted);
+    if (obj == NULL)
+        goto error;
+    PyStructSequence_SET_ITEM(result, 2, obj);
+
+    assert(info.resolution > 0.0);
+    assert(info.resolution <= 1.0);
+    obj = PyFloat_FromDouble(info.resolution);
+    if (obj == NULL)
+        goto error;
+    PyStructSequence_SET_ITEM(result, 3, obj);
+
+    return result;
+
+error:
+    Py_DECREF(result);
+    return NULL;
+}
+
+PyDoc_STRVAR(get_clock_info_doc,
+"get_clock_info(name: str) -> dict\n\
+\n\
+Get information of the specified clock.");
 
 static void
 PyInit_timezone(PyObject *m) {
@@ -977,10 +1321,8 @@
 #endif /* __CYGWIN__ */
 #endif /* !HAVE_TZNAME || __GLIBC__ || __CYGWIN__*/
 
-#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_GETRES)
-#ifdef CLOCK_REALTIME
+#if defined(HAVE_CLOCK_GETTIME)
     PyModule_AddIntMacro(m, CLOCK_REALTIME);
-#endif
 #ifdef CLOCK_MONOTONIC
     PyModule_AddIntMacro(m, CLOCK_MONOTONIC);
 #endif
@@ -1002,7 +1344,7 @@
 
 static PyMethodDef time_methods[] = {
     {"time",            time_time, METH_NOARGS, time_doc},
-#if (defined(MS_WINDOWS) && !defined(__BORLANDC__)) || defined(HAVE_CLOCK)
+#ifdef PYCLOCK
     {"clock",           time_clock, METH_NOARGS, clock_doc},
 #endif
 #ifdef HAVE_CLOCK_GETTIME
@@ -1018,8 +1360,6 @@
 #ifdef HAVE_MKTIME
     {"mktime",          time_mktime, METH_O, mktime_doc},
 #endif
-    {"steady",          (PyCFunction)time_steady, METH_VARARGS|METH_KEYWORDS,
-                        steady_doc},
 #ifdef HAVE_STRFTIME
     {"strftime",        time_strftime, METH_VARARGS, strftime_doc},
 #endif
@@ -1027,6 +1367,12 @@
 #ifdef HAVE_WORKING_TZSET
     {"tzset",           time_tzset, METH_NOARGS, tzset_doc},
 #endif
+#ifdef PYMONOTONIC
+    {"monotonic",       time_monotonic, METH_NOARGS, monotonic_doc},
+#endif
+    {"process_time",    time_process_time, METH_NOARGS, process_time_doc},
+    {"perf_counter",    time_perf_counter, METH_NOARGS, perf_counter_doc},
+    {"get_clock_info",  time_get_clock_info, METH_VARARGS, get_clock_info_doc},
     {NULL,              NULL}           /* sentinel */
 };
 
@@ -1104,6 +1450,20 @@
     if (!initialized) {
         PyStructSequence_InitType(&StructTimeType,
                                   &struct_time_type_desc);
+
+        /* initialize ClockInfoType */
+        PyStructSequence_InitType(&ClockInfoType, &ClockInfo_desc);
+        Py_INCREF(&ClockInfoType);
+        PyModule_AddObject(m, "clock_info", (PyObject*)&ClockInfoType);
+
+#ifdef MS_WINDOWS
+        winver.dwOSVersionInfoSize = sizeof(winver);
+        if (!GetVersionEx((OSVERSIONINFO*)&winver)) {
+            Py_DECREF(m);
+            PyErr_SetFromWindowsErr(0);
+            return NULL;
+        }
+#endif
     }
     Py_INCREF(&StructTimeType);
     PyModule_AddObject(m, "struct_time", (PyObject*) &StructTimeType);
@@ -1112,7 +1472,7 @@
 }
 
 static PyObject*
-floattime(void)
+floattime(_Py_clock_info_t *info)
 {
     _PyTime_timeval t;
 #ifdef HAVE_CLOCK_GETTIME
@@ -1123,10 +1483,21 @@
        because it would require to link Python to the rt (real-time)
        library, at least on Linux */
     ret = clock_gettime(CLOCK_REALTIME, &tp);
-    if (ret == 0)
+    if (ret == 0) {
+        if (info) {
+            struct timespec res;
+            info->implementation = "clock_gettime(CLOCK_REALTIME)";
+            info->is_monotonic = 0;
+            info->is_adjusted = 1;
+            if (clock_getres(CLOCK_REALTIME, &res) == 0)
+                info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
+            else
+                info->resolution = 1e-9;
+        }
         return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
+    }
 #endif
-    _PyTime_gettimeofday(&t);
+    _PyTime_gettimeofday_info(&t, info);
     return PyFloat_FromDouble((double)t.tv_sec + t.tv_usec * 1e-6);
 }
 
diff --git a/Python/pytime.c b/Python/pytime.c
index db3f683..ddd3088 100644
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -18,8 +18,8 @@
 extern int ftime(struct timeb *);
 #endif
 
-void
-_PyTime_gettimeofday(_PyTime_timeval *tp)
+static void
+pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info)
 {
 #ifdef MS_WINDOWS
     FILETIME system_time;
@@ -35,6 +35,20 @@
     microseconds = large.QuadPart / 10 - 11644473600000000;
     tp->tv_sec = microseconds / 1000000;
     tp->tv_usec = microseconds % 1000000;
+    if (info) {
+        DWORD timeAdjustment, timeIncrement;
+        BOOL isTimeAdjustmentDisabled;
+
+        info->implementation = "GetSystemTimeAsFileTime()";
+        info->is_monotonic = 0;
+        (void) GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
+                                       &isTimeAdjustmentDisabled);
+        info->resolution = timeIncrement * 1e-7;
+        if (isTimeAdjustmentDisabled)
+            info->is_adjusted = 0;
+        else
+            info->is_adjusted = 1;
+    }
 #else
     /* There are three ways to get the time:
       (1) gettimeofday() -- resolution in microseconds
@@ -46,14 +60,22 @@
       Note: clock resolution does not imply clock accuracy! */
 
 #ifdef HAVE_GETTIMEOFDAY
+    int err;
 #ifdef GETTIMEOFDAY_NO_TZ
-    if (gettimeofday(tp) == 0)
+    err = gettimeofday(tp);
+#else
+    err = gettimeofday(tp, (struct timezone *)NULL);
+#endif
+    if (err == 0) {
+        if (info) {
+            info->implementation = "gettimeofday()";
+            info->resolution = 1e-6;
+            info->is_monotonic = 0;
+            info->is_adjusted = 1;
+        }
         return;
-#else /* !GETTIMEOFDAY_NO_TZ */
-    if (gettimeofday(tp, (struct timezone *)NULL) == 0)
-        return;
-#endif /* !GETTIMEOFDAY_NO_TZ */
-#endif /* !HAVE_GETTIMEOFDAY */
+    }
+#endif   /* HAVE_GETTIMEOFDAY */
 
 #if defined(HAVE_FTIME)
     {
@@ -61,15 +83,39 @@
         ftime(&t);
         tp->tv_sec = t.time;
         tp->tv_usec = t.millitm * 1000;
+        if (info) {
+            info->implementation = "ftime()";
+            info->resolution = 1e-3;
+            info->is_monotonic = 0;
+            info->is_adjusted = 1;
+        }
     }
 #else /* !HAVE_FTIME */
     tp->tv_sec = time(NULL);
     tp->tv_usec = 0;
+    if (info) {
+        info->implementation = "time()";
+        info->resolution = 1.0;
+        info->is_monotonic = 0;
+        info->is_adjusted = 1;
+    }
 #endif /* !HAVE_FTIME */
 
 #endif /* MS_WINDOWS */
 }
 
+void
+_PyTime_gettimeofday(_PyTime_timeval *tp)
+{
+    pygettimeofday(tp, NULL);
+}
+
+void
+_PyTime_gettimeofday_info(_PyTime_timeval *tp, _Py_clock_info_t *info)
+{
+    pygettimeofday(tp, info);
+}
+
 static void
 error_time_t_overflow(void)
 {