diff -r 58eef400866e Doc/library/os.rst --- a/Doc/library/os.rst Sat Mar 03 16:20:37 2012 +0000 +++ b/Doc/library/os.rst Sun Mar 04 00:59:33 2012 +0100 @@ -880,7 +880,9 @@ as internal buffering of data. Like :func:`utime` but if *path* is relative, it is taken as relative to *dirfd*. If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path* is interpreted relative to the current working directory. *times* must be a - 2-tuple of numbers, of the form ``(atime, mtime)``, or None. + 2-tuple of numbers, of the form ``(atime, mtime)``, or None. atime and mtime + are :type:`int`, :type:`float` or a (sec, nsec) tuple where sec and nsec are + :type:`int` and nsec is in the range [0; 999999999]. Availability: Unix. @@ -920,8 +922,10 @@ as internal buffering of data. Set the access and modified time of the file specified by the file descriptor *fd* to the given values. *atimes* must be a 2-tuple of numbers, - of the form ``(atime, mtime)``, or None. If no second argument is used, - set the access and modified times to the current time. + of the form ``(atime, mtime)``, or None. atime and mtime are :type:`int`, + :type:`float` or a (sec, nsec) tuple where sec and nsec are :type:`int` and + nsec is in the range [0; 999999999]. If no second argument is used, set the + access and modified times to the current time. Availability: Unix. @@ -1748,7 +1752,9 @@ Files and Directories Like :func:`utime`, but if *path* is a symbolic link, it is not dereferenced. *times* must be a 2-tuple of numbers, of the form - ``(atime, mtime)``, or None. + ``(atime, mtime)``, or None. atime and mtime are :type:`int`, :type:`float` + or a (sec, nsec) tuple where sec and nsec are :type:`int` and nsec is in the + range [0; 999999999]. Availability: Unix. @@ -2195,7 +2201,10 @@ Files and Directories set to the current time. (The effect is similar to running the Unix program :program:`touch` on the path.) Otherwise, *times* must be a 2-tuple of numbers, of the form ``(atime, mtime)`` which is used to set the access and - modified times, respectively. Whether a directory can be given for *path* + modified times, respectively. atime and mtime are :type:`int`, :type:`float` + or a (sec, nsec) tuple where sec and nsec are :type:`int` and nsec is in the + range [0; 999999999]. + Whether a directory can be given for *path* depends on whether the operating system implements directories as files (for example, Windows does not). Note that the exact times you set here may not be returned by a subsequent :func:`~os.stat` call, depending on the @@ -2204,6 +2213,12 @@ Files and Directories Availability: Unix, Windows. + .. note:: + To copy the access and modification time of a file with nanosecond + resolution, don't use :type:`int` on st_atime and st_mtime but + :func:`math.floor` to round correctly negative timestamps. See also + :func:`shutil.copystat`. + .. function:: walk(top, topdown=True, onerror=None, followlinks=False) diff -r 58eef400866e Lib/shutil.py --- a/Lib/shutil.py Sat Mar 03 16:20:37 2012 +0000 +++ b/Lib/shutil.py Sun Mar 04 00:59:33 2012 +0100 @@ -4,13 +4,14 @@ XXX The functions here don't copy the re """ -import os -import sys -import stat from os.path import abspath -import fnmatch import collections import errno +import fnmatch +import math +import os +import stat +import sys import tarfile try: @@ -154,7 +155,9 @@ def copystat(src, dst, symlinks=False): st = stat_func(src) mode = stat.S_IMODE(st.st_mode) - utime_func(dst, (st.st_atime, st.st_mtime)) + atime = (math.floor(st.st_atime), st.st_atime_ns) + mtime = (math.floor(st.st_mtime), st.st_mtime_ns) + utime_func(dst, (atime, mtime)) chmod_func(dst, mode) if hasattr(st, 'st_flags'): try: diff -r 58eef400866e Modules/posixmodule.c --- a/Modules/posixmodule.c Sat Mar 03 16:20:37 2012 +0000 +++ b/Modules/posixmodule.c Sun Mar 04 00:59:33 2012 +0100 @@ -3541,28 +3560,38 @@ posix_uname(PyObject *self, PyObject *no static int -extract_time(PyObject *t, time_t* sec, long* nsec) -{ - time_t intval; - if (PyFloat_Check(t)) { - double d = PyFloat_AsDouble(t); - double mod; - *sec = (time_t)d; - mod = fmod(d, 1.0); - mod *= 1e9; - *nsec = (long)mod; +extract_time(PyObject *obj, time_t* sec, long* nsec) +{ + PyObject *item; + long value; + + if (PyTuple_Check(obj)) { + if (PyTuple_Size(obj) != 2 + || !PyLong_Check(PyTuple_GET_ITEM(obj, 0)) + || !PyLong_Check(PyTuple_GET_ITEM(obj, 1))) { + PyErr_SetString(PyExc_TypeError, + "atime and mtime must be an int, a float " + "or a tuple of 2 integers"); + return -1; + } + item = PyTuple_GET_ITEM(obj, 0); + *sec = PyLong_AsTime_t(item); + if (*sec == (time_t)-1 && PyErr_Occurred()) + return -1; + item = PyTuple_GET_ITEM(obj, 1); + value = PyLong_AsTime_t(item); + if (value == -1 && PyErr_Occurred()) + return -1; + if (value < 0 || value > 999999999) { + PyErr_SetString(PyExc_ValueError, + "nanosecond must be in range [0; 999999999]"); + return -1; + } + *nsec = value; return 0; } -#if SIZEOF_TIME_T > SIZEOF_LONG - intval = PyLong_AsUnsignedLongLongMask(t); -#else - intval = PyLong_AsLong(t); -#endif - if (intval == -1 && PyErr_Occurred()) - return -1; - *sec = intval; - *nsec = 0; - return 0; + else + return _PyTime_ObjectToTimespec(obj, sec, nsec); } PyDoc_STRVAR(posix_utime__doc__,