12

Python's os.path.getctime on the Mac (and under Unix in general) does not give the date when a file was created but "the time of the last change" (according to the docs at least). On the other hand in the Finder I can see the real file creation time so this information is kept by HFS+.

Do you have any suggestions on how to obtain the file creation time on the Mac in a Python program?

cefstat
  • 2,336
  • 1
  • 18
  • 25
  • Duplicate: http://stackoverflow.com/questions/237079/how-to-get-file-creation-modification-date-times-in-python – S.Lott Jun 03 '09 at 21:20
  • 1
    @S.Lott: not really, since getting the file creation time on a Mac is inherently non-cross-platform. – Miles Jun 03 '09 at 21:25
  • @Miles: perhaps true, but the answer there applies to this question perfectly. – S.Lott Jun 03 '09 at 21:29
  • 1
    @S.Lott: it's not a duplicate and believe me that the answer there does not apply *at all* to the Mac (unless you accept that the answer is that there is no way to get the creation time on the Mac which Miles has proven wrong). On the Mac both os.path.getctime and os.stat give the last *change* time and not the creation time. So everybody that says that os.stat is the cross-platform method of getting the creation time is wrong. – cefstat Jun 04 '09 at 08:04

3 Answers3

25

Use the st_birthtime property on the result of a call to os.stat() (or fstat/lstat).

def get_creation_time(path):
    return os.stat(path).st_birthtime

You can convert the integer result to a datetime object using datetime.datetime.fromtimestamp().

For some reason I don't think this worked on Mac OS X when this answer was first written, but I could be mistaken, and it does work now, even with older versions of Python. The old answer is below for posterity.


Using ctypes to access the system call stat64 (works with Python 2.5+):

from ctypes import *

class struct_timespec(Structure):
    _fields_ = [('tv_sec', c_long), ('tv_nsec', c_long)]

class struct_stat64(Structure):
    _fields_ = [
        ('st_dev', c_int32),
        ('st_mode', c_uint16),
        ('st_nlink', c_uint16),
        ('st_ino', c_uint64),
        ('st_uid', c_uint32),
        ('st_gid', c_uint32), 
        ('st_rdev', c_int32),
        ('st_atimespec', struct_timespec),
        ('st_mtimespec', struct_timespec),
        ('st_ctimespec', struct_timespec),
        ('st_birthtimespec', struct_timespec),
        ('dont_care', c_uint64 * 8)
    ]

libc = CDLL('libc.dylib') # or /usr/lib/libc.dylib
stat64 = libc.stat64
stat64.argtypes = [c_char_p, POINTER(struct_stat64)]

def get_creation_time(path):
    buf = struct_stat64()
    rv = stat64(path, pointer(buf))
    if rv != 0:
        raise OSError("Couldn't stat file %r" % path)
    return buf.st_birthtimespec.tv_sec

Using subprocess to call the stat utility:

import subprocess

def get_creation_time(path):
    p = subprocess.Popen(['stat', '-f%B', path],
        stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    if p.wait():
        raise OSError(p.stderr.read().rstrip())
    else:
        return int(p.stdout.read())
Miles
  • 31,360
  • 7
  • 64
  • 74
  • 2
    On El Capitan, you might want to use `libc = CDLL('/usr/lib/libc.dylib')` instead of `libc = CDLL('libc.dylib')` if you get an `OSError: dlopen(libc.dylib, 6): image not found`. – Thomas Orozco Oct 07 '15 at 08:43
  • @ThomasOrozco Turns out there's a better approach than ctypes anyway. – Miles Oct 08 '15 at 01:34
3

By lack of a good utility, I've created crtime.

pip install crtime

Then you can use it like:

sudo crtime ./

Would print:

1552938281  /home/pascal/crtime/.gitignore
1552938281  /home/pascal/crtime/README.md
1552938281  /home/pascal/crtime/crtime
1552938281  /home/pascal/crtime/deploy.py
1552938281  /home/pascal/crtime/setup.cfg
1552938281  /home/pascal/crtime/setup.py
1552938961  /home/pascal/crtime/crtime.egg-info
1552939447  /home/pascal/crtime/.git
1552939540  /home/pascal/crtime/build
1552939540  /home/pascal/crtime/dist

Note that for large directories it will be easily 1000x faster than xstat that is sometimes mentioned, as this creates a temporary file and then executes stat calls for all files at once.

PascalVKooten
  • 20,643
  • 17
  • 103
  • 160
3

ctime differs on the platform: On some systems (like Unix) is the time of the last metadata change, and, on others (like Windows), is the creation time. That's because Unices usually don't preserve the "original" creation time.

That said you can access all information that the OS provides with the stat module.

The stat module defines constants and functions for interpreting the results of os.stat(), os.fstat() and os.lstat() (if they exist). For complete details about the stat, fstat and lstat calls, consult the documentation for your system.

stat.ST_CTIME
The “ctime” as reported by the operating system. On some systems (like Unix) is the time of the last metadata change, and, on others (like Windows), is the creation time (see platform documentation for details).

Community
  • 1
  • 1
lothar
  • 19,853
  • 5
  • 45
  • 59
  • Thanks but... stat.ST_CTIME: The “ctime” as reported by the operating system. On some systems (like Unix) is the time of the last metadata change, and, on others (like Windows), is the creation time (see platform documentation for details). – cefstat Jun 03 '09 at 20:23
  • 1
    @cefstat That's the point. Some systems (like unices) simply do _not_ provide the "original" creation time. There's nothing python can do about that. – lothar Jun 03 '09 at 20:25