Skip to content

Commit

Permalink
Add st_birthtime_ns and deprecate st_ctime instead of changing it
Browse files Browse the repository at this point in the history
  • Loading branch information
zooba committed Feb 28, 2023
1 parent 00460d8 commit a01fb67
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 41 deletions.
71 changes: 48 additions & 23 deletions Doc/library/os.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2791,8 +2791,10 @@ features:
for :class:`bytes` paths on Windows.

.. versionchanged:: 3.12
The ``st_ctime`` attributes of a stat result are now set to zero, rather
than being incorrectly set to the creation time of the file.
The ``st_ctime`` attribute of a stat result is deprecated on Windows.
The file creation time is properly available as ``st_birthtime``, and
in the future ``st_ctime`` may be changed to return zero or the
metadata change time, if available.


.. function:: stat(path, *, dir_fd=None, follow_symlinks=True)
Expand Down Expand Up @@ -2911,6 +2913,11 @@ features:

Time of most recent metadata change expressed in seconds.

.. versionchanged:: 3.12
``st_ctime`` is deprecated on Windows. Use ``st_birthtime`` for
the file creation time. In the future, ``st_ctime`` will contain
the time of the most recent metadata change, as for other platforms.

.. attribute:: st_atime_ns

Time of most recent access expressed in nanoseconds as an integer.
Expand All @@ -2925,23 +2932,45 @@ features:
Time of most recent metadata change expressed in nanoseconds as an
integer.

.. versionchanged:: 3.12
``st_ctime_ns`` is deprecated on Windows. Use ``st_birthtime_ns``
for the file creation time. In the future, ``st_ctime`` will contain
the time of the most recent metadata change, as for other platforms.

.. attribute:: st_birthtime

Time of file creation expressed in seconds. This attribute is not
always available, and may raise :exc:`AttributeError`.

.. versionchanged:: 3.12
``st_birthtime`` is now available on Windows.

.. attribute:: st_birthtime_ns

Time of file creation expressed in nanoseconds as an integer.
This attribute is not always available, and may raise
:exc:`AttributeError`.

.. versionadded:: 3.12

.. note::

The exact meaning and resolution of the :attr:`st_atime`,
:attr:`st_mtime`, and :attr:`st_ctime` attributes depend on the operating
system and the file system. For example, on Windows systems using the FAT
or FAT32 file systems, :attr:`st_mtime` has 2-second resolution, and
:attr:`st_atime` has only 1-day resolution. See your operating system
documentation for details.
:attr:`st_mtime`, :attr:`st_ctime` and :attr:`st_birthtime` attributes
depend on the operating system and the file system. For example, on
Windows systems using the FAT32 file systems, :attr:`st_mtime` has
2-second resolution, and :attr:`st_atime` has only 1-day resolution.
See your operating system documentation for details.

Similarly, although :attr:`st_atime_ns`, :attr:`st_mtime_ns`,
and :attr:`st_ctime_ns` are always expressed in nanoseconds, many
systems do not provide nanosecond precision. On systems that do
provide nanosecond precision, the floating-point object used to
store :attr:`st_atime`, :attr:`st_mtime`, and :attr:`st_ctime`
cannot preserve all of it, and as such will be slightly inexact.
If you need the exact timestamps you should always use
:attr:`st_atime_ns`, :attr:`st_mtime_ns`, and :attr:`st_ctime_ns`.
:attr:`st_ctime_ns` and :attr:`st_birthtime_ns` are always expressed in
nanoseconds, many systems do not provide nanosecond precision. On
systems that do provide nanosecond precision, the floating-point object
used to store :attr:`st_atime`, :attr:`st_mtime`, :attr:`st_ctime` and
:attr:`st_birthtime` cannot preserve all of it, and as such will be
slightly inexact. If you need the exact timestamps you should always use
:attr:`st_atime_ns`, :attr:`st_mtime_ns`, :attr:`st_ctime_ns` and
:attr:`st_birthtime_ns`.

On some Unix systems (such as Linux), the following attributes may also be
available:
Expand Down Expand Up @@ -2971,10 +3000,6 @@ features:

File generation number.

.. attribute:: st_birthtime

Time of file creation. This is also available on Windows.

On Solaris and derivatives, the following attributes may also be
available:

Expand All @@ -2997,8 +3022,7 @@ features:

File type.

On Windows systems, as well as ``st_birthtime`` above, the following
attributes are also available:
On Windows systems, the following attributes are also available:

.. attribute:: st_file_attributes

Expand Down Expand Up @@ -3049,9 +3073,10 @@ features:
as appropriate.

.. versionchanged:: 3.12
On Windows, :attr:`st_ctime` now also contains the last metadata
change time, for consistency with other platforms.
Use :attr:`st_birthtime` for the creation time when available.
On Windows, :attr:`st_ctime` is deprecated. Eventually, it will
contain the last metadata change time, for consistency with other
platforms, but for now still contains creation time.
Use :attr:`st_birthtime` for the creation time.

.. versionchanged:: 3.12
On Windows, :attr:`st_ino` may now be up to 128 bits, depending
Expand Down
52 changes: 36 additions & 16 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -2071,6 +2071,10 @@ win32_xstat(const wchar_t *path, struct _Py_stat_struct *result, BOOL traverse)
setting it to a POSIX error. Callers should use GetLastError. */
int code = win32_xstat_impl(path, result, traverse);
errno = 0;

/* ctime is only deprecated from 3.12, so we copy birthtime across */
result->st_ctime = result->st_birthtime;
result->st_ctime_nsec = result->st_birthtime_nsec;
return code;
}
/* About the following functions: win32_lstat_w, win32_stat, win32_stat_w
Expand Down Expand Up @@ -2144,6 +2148,9 @@ static PyStructSequence_Field stat_result_fields[] = {
#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) || defined(MS_WINDOWS)
{"st_birthtime", "time of creation"},
#endif
#ifdef MS_WINDOWS
{"st_birthtime_ns", "time of creation in nanoseconds"},
#endif
#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES
{"st_file_attributes", "Windows file attribute bits"},
#endif
Expand Down Expand Up @@ -2192,10 +2199,16 @@ static PyStructSequence_Field stat_result_fields[] = {
#define ST_BIRTHTIME_IDX ST_GEN_IDX
#endif

#ifdef MS_WINDOWS
#define ST_BIRTHTIME_NS_IDX (ST_BIRTHTIME_IDX+1)
#else
#define ST_BIRTHTIME_NS_IDX ST_BIRTHTIME_IDX
#endif

#if defined(HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES) || defined(MS_WINDOWS)
#define ST_FILE_ATTRIBUTES_IDX (ST_BIRTHTIME_IDX+1)
#define ST_FILE_ATTRIBUTES_IDX (ST_BIRTHTIME_NS_IDX+1)
#else
#define ST_FILE_ATTRIBUTES_IDX ST_BIRTHTIME_IDX
#define ST_FILE_ATTRIBUTES_IDX ST_BIRTHTIME_NS_IDX
#endif

#ifdef HAVE_STRUCT_STAT_ST_FSTYPE
Expand Down Expand Up @@ -2364,7 +2377,7 @@ _posix_free(void *module)
}

static void
fill_time(PyObject *module, PyObject *v, int index, time_t sec, unsigned long nsec)
fill_time(PyObject *module, PyObject *v, int s_index, int f_index, int ns_index, time_t sec, unsigned long nsec)
{
PyObject *s = _PyLong_FromTime_t(sec);
PyObject *ns_fractional = PyLong_FromUnsignedLong(nsec);
Expand All @@ -2388,12 +2401,18 @@ fill_time(PyObject *module, PyObject *v, int index, time_t sec, unsigned long ns
goto exit;
}

PyStructSequence_SET_ITEM(v, index, s);
PyStructSequence_SET_ITEM(v, index+3, float_s);
PyStructSequence_SET_ITEM(v, index+6, ns_total);
s = NULL;
float_s = NULL;
ns_total = NULL;
if (s_index >= 0) {
PyStructSequence_SET_ITEM(v, s_index, s);
s = NULL;
}
if (f_index >= 0) {
PyStructSequence_SET_ITEM(v, f_index, float_s);
float_s = NULL;
}
if (ns_index >= 0) {
PyStructSequence_SET_ITEM(v, ns_index, ns_total);
ns_total = NULL;
}
exit:
Py_XDECREF(s);
Py_XDECREF(ns_fractional);
Expand Down Expand Up @@ -2477,9 +2496,9 @@ _pystat_fromstructstat(PyObject *module, STRUCT_STAT *st)
#else
ansec = mnsec = cnsec = 0;
#endif
fill_time(module, v, 7, st->st_atime, ansec);
fill_time(module, v, 8, st->st_mtime, mnsec);
fill_time(module, v, 9, st->st_ctime, cnsec);
fill_time(module, v, 7, 10, 13, st->st_atime, ansec);
fill_time(module, v, 8, 11, 14, st->st_mtime, mnsec);
fill_time(module, v, 9, 12, 15, st->st_ctime, cnsec);

#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
PyStructSequence_SET_ITEM(v, ST_BLKSIZE_IDX,
Expand All @@ -2497,14 +2516,12 @@ _pystat_fromstructstat(PyObject *module, STRUCT_STAT *st)
PyStructSequence_SET_ITEM(v, ST_GEN_IDX,
PyLong_FromLong((long)st->st_gen));
#endif
#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) || defined(MS_WINDOWS)
#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME)
{
PyObject *val;
unsigned long bsec,bnsec;
bsec = (long)st->st_birthtime;
#ifdef MS_WINDOWS
bnsec = st->st_birthtime_nsec;
#elif defined(HAVE_STAT_TV_NSEC2)
#ifdef HAVE_STAT_TV_NSEC2
bnsec = st->st_birthtimespec.tv_nsec;
#else
bnsec = 0;
Expand All @@ -2513,6 +2530,9 @@ _pystat_fromstructstat(PyObject *module, STRUCT_STAT *st)
PyStructSequence_SET_ITEM(v, ST_BIRTHTIME_IDX,
val);
}
#elif defined(MS_WINDOWS)
fill_time(module, v, ST_BIRTHTIME_IDX, -1, ST_BIRTHTIME_NS_IDX,
st->st_birthtime, st->st_birthtime_nsec);
#endif
#ifdef HAVE_STRUCT_STAT_ST_FLAGS
PyStructSequence_SET_ITEM(v, ST_FLAGS_IDX,
Expand Down
3 changes: 1 addition & 2 deletions Python/fileutils.c
Original file line number Diff line number Diff line change
Expand Up @@ -1096,15 +1096,14 @@ _Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *info, ULONG reparse_tag,
result->st_size = (((__int64)info->nFileSizeHigh)<<32) + info->nFileSizeLow;
result->st_dev = id_info ? id_info->VolumeSerialNumber : info->dwVolumeSerialNumber;
result->st_rdev = 0;
/* st_ctime is deprecated, but we preserve the legacy value in our caller, not here */
if (basic_info) {
LARGE_INTEGER_to_time_t_nsec(&basic_info->CreationTime, &result->st_birthtime, &result->st_birthtime_nsec);
LARGE_INTEGER_to_time_t_nsec(&basic_info->ChangeTime, &result->st_ctime, &result->st_ctime_nsec);
LARGE_INTEGER_to_time_t_nsec(&basic_info->LastWriteTime, &result->st_mtime, &result->st_mtime_nsec);
LARGE_INTEGER_to_time_t_nsec(&basic_info->LastAccessTime, &result->st_atime, &result->st_atime_nsec);
} else {
FILE_TIME_to_time_t_nsec(&info->ftCreationTime, &result->st_birthtime, &result->st_birthtime_nsec);
/* We leave ctime as zero because we do not have it without FILE_BASIC_INFO.
Our callers will replace it with birthtime if they want legacy behaviour */
FILE_TIME_to_time_t_nsec(&info->ftLastWriteTime, &result->st_mtime, &result->st_mtime_nsec);
FILE_TIME_to_time_t_nsec(&info->ftLastAccessTime, &result->st_atime, &result->st_atime_nsec);
}
Expand Down

0 comments on commit a01fb67

Please sign in to comment.