GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Windows os.stat and os.fstat report different st_ctime_ns values · Issue #157671 · python/cpython

Windows os.stat and os.fstat report different st_ctime_ns values #157671

New issue
Copy link
New issue
Copy link
Open
Open
Windows os.stat and os.fstat report different st_ctime_ns values#157671
Copy link
Labels
OS-windowsextension-modulesC modules in the Modules dirC modules in the Modules dir

Description

@jezdez
jezdez
opened on Sep 17, 2026
Issue body actions

Bug description:

On Windows, os.stat(path).st_ctime_ns and os.fstat(fd).st_ctime_ns can differ for the same file.

I ran the example below on Windows 11 with NTFS. It fails with both official Python 3.14.7 Windows builds: ARM64 running natively and x64 running under emulation. The same-file assertion passes, but the timestamp assertion fails. Both st_birthtime_ns values equal os.stat(path).st_ctime_ns.

In the 3.14.7 code:

  • _Py_attribute_data_to_stat assigns FILE_BASIC_INFO.ChangeTime to st_ctime.
  • _Py_fstat_noraise returns that result. os_fstat_impl does not change the timestamp.
  • win32_xstat replaces st_ctime and its nanoseconds with st_birthtime for compatibility.

Commit a01fb672 added that compatibility step. The discussion in #102149 says st_ctime should continue returning creation time during the deprecation period. Should fstat() do the same?

In #117354, DirEntry.stat() was changed to return creation time during the same deprecation period, following #117267.

Example:

import os
import time
from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as directory:
    path = Path(directory) / "example.txt"
    path.write_text("before")
    time.sleep(0.1)
    path.write_text("after")

    with path.open("rb") as source:
        by_path = os.stat(path)
        by_fd = os.fstat(source.fileno())

    print("stat ctime:", by_path.st_ctime_ns)
    print("fstat ctime:", by_fd.st_ctime_ns)
    print("stat birthtime:", getattr(by_path, "st_birthtime_ns", None))
    print("fstat birthtime:", getattr(by_fd, "st_birthtime_ns", None))
    assert os.path.samestat(by_path, by_fd)
    assert by_path.st_ctime_ns == by_fd.st_ctime_ns

Output from the official Python 3.14.7 ARM64 build:

stat ctime: 1789643317486473600
fstat ctime: 1789643317591142500
stat birthtime: 1789643317486473600
fstat birthtime: 1789643317486473600

The final assertion raises AssertionError.

CPython versions tested on:

  • 3.10.20 (conda-forge build): timestamps match.
  • 3.14.6 (Anaconda build): timestamps differ.
  • 3.14.7: timestamps differ with both official Windows embeddable builds, ARM64 and x64.

Operating systems tested on:

Windows 11 ARM64, build 26200.9457, in Parallels Desktop. The test files were on NTFS. The ARM64 Python build ran natively and the x64 build ran under Windows emulation.

Activity

  1. jezdez
    changed the title [-]Windows os.stat and os.fstat report different st_ctime values[/-] [+]Windows os.stat and os.fstat may report different st_ctime values[/+] on Sep 17, 2026
  2. jezdez
    changed the title [-]Windows os.stat and os.fstat may report different st_ctime values[/-] [+]Windows os.stat and os.fstat report different st_ctime_ns values[/+] on Sep 17, 2026
  3. jezdez commented on Sep 17, 2026

    @jezdez
    jezdez
    on Sep 17, 2026
    Author
    More actions

    Hey @zooba, could I get your thoughts on this before I start on a patch? My reading of #102149 is that fstat() should still return creation time during the deprecation period. Have I understood that correctly?

    I was thinking of putting the adjustment in _pystat_fromstructstat(), so it covers both os.fstat(fd) and os.stat(fd), and removing the existing copies in win32_xstat() and DirEntry_from_find_data().

    Does that sound reasonable to you? I'm also unsure about backports, given how long fstat() has returned change time. Thanks!

  4. aisk
    added
    OS-windows
    extension-modulesC modules in the Modules dirC modules in the Modules dir
    on Sep 17, 2026
  5. aisk
    added this to OS issues 💻on Sep 17, 2026
  6. github-project-automation
    moved this to Todo in OS issues 💻on Sep 17, 2026
  7. zooba commented on Sep 17, 2026

    @zooba
    zooba
    on Sep 17, 2026
    Member
    More actions

    I don't remember if we set a date on switching to change time everywhere, but I'd prefer to do that if possible.

    Given this is the first time anyone has noticed the fstat change, I suspect we're probably going to be okay to update stat as well. In any case, I don't think we've got any way to prove that it's safe to change here, so probably changing it in 3.16 and watching for complaints during its prerelease period is the way.

    I'm interested: are you trying to use creation time or change time? All the examples of use I could find for st_ctime were assuming it meant creation time and that macOS was special for having birth time, with a few bugs relating to ctime on Linux not actually being the creation time. It's very hard to guess what we should try to be consistent with here.

  8. jezdez commented on Sep 17, 2026

    @jezdez
    jezdez
    on Sep 17, 2026
    Author
    More actions

    Thanks, Steve! Change time is what I’m after here. I’m working on conda’s config-file writer and checking whether a file changed while reading it or before replacing it. ctime is one of several checks. I hit this because I compared stat() and fstat() results assuming their st_ctime_ns fields meant the same thing.

    Moving stat() to change time in 3.16 and seeing what comes back during the prereleases sounds good to me. I can handle the difference in conda for existing Python versions, so I don’t need a backport for this.

    Reacted by Steve Dower
  9. zooba commented on Sep 17, 2026

    @zooba
    zooba
    on Sep 17, 2026
    Member
    More actions

    You might just be best off in your case to consistently use fstat, and check mtime as well as ctime. I'm not sure that change time has universal meaning on Windows to begin with - it's almost certainly different between file systems (I'm pretty sure FAT never touches it, for example).

    The typical approach on Windows is to just keep the file open and prevent anyone else from modifying it, which is why the metadata approach is harder. Python doesn't really support the fine-grained file sharing options though, since it's based on POSIX.

  10. Xander-git
    added 2 commits that reference this issue on Sep 30, 2026

    fix(sdk): measure the path-branch publication token through the open …

    416457a

    fix(sdk): measure the path-branch publication token through the open …

    26d087a
  11. yaelysia
    added a commit that references this issue on Oct 19, 2026

    fix(remote): compare compatible Windows stat identities without weake…

    555c7c1
  12. loonghao
    added 2 commits that reference this issue on Oct 20, 2026

    fix: compare PNG file timestamps through consistent APIs

    3aa9b76

    fix: compare PNG file timestamps through consistent APIs

    1ba474f
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    OS-windowsextension-modulesC modules in the Modules dirC modules in the Modules dir

    Projects

    • OS issues 💻
      Status
      Todo
      Show more project fields

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    You are visitor 01402503 — GeezerVison does not store the pages you look up.
    Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.