PEP 410
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 8b2b211..0ab63c3 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -1702,6 +1702,12 @@
     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 @@
 }
 
 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 @@
 #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 @@
 /* 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 @@
     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,
@@ -1801,21 +1831,26 @@
 #endif
 #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
     {
-      PyObject *val;
-      unsigned long bsec,bnsec;
-      bsec = (long)st->st_birthtime;
+        PyObject *val;
+        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);
-      }
-      PyStructSequence_SET_ITEM(v, ST_BIRTHTIME_IDX,
-                                val);
+        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);
     }
 #endif
 #ifdef HAVE_STRUCT_STAT_ST_FLAGS
@@ -1832,7 +1867,7 @@
 }
 
 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 @@
               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 @@
 
         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 @@
 #endif
     }
     else
-        result = _pystat_fromstructstat(&st);
+        result = _pystat_fromstructstat(&st, timestamp);
 
     Py_DECREF(opath);
     return result;
@@ -3381,16 +3421,16 @@
 
 
 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 @@
 
 #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 @@
 #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 @@
 
 #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 @@
 
 
 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 @@
 #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 @@
 #endif
     }
 
-    return _pystat_fromstructstat(&st);
+    return _pystat_fromstructstat(&st, timestamp);
 }
 
 PyDoc_STRVAR(posix_isatty__doc__,
@@ -9634,22 +9689,25 @@
 
 #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 @@
     if (res != 0)
         return posix_error();
 
-    return _pystat_fromstructstat(&st);
+    return _pystat_fromstructstat(&st, timestamp);
 }
 #endif
 
@@ -10524,7 +10582,7 @@
 #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 @@
     {"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 @@
     {"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 @@
     {"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 @@
     {"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__},