What's the best cross-platform way to get file creation and modification dates/times, that works on both Linux and Windows?
In Python 3.4 and above, you can use the object oriented pathlib module interface which includes wrappers for much of the os module. Here is an example of getting the file stats.
>>> import pathlib
>>> fname = pathlib.Path('test.py')
>>> assert fname.exists(), f'No such file: {fname}' # check that the file exists
>>> print(fname.stat())
os.stat_result(st_mode=33206, st_ino=5066549581564298, st_dev=573948050, st_nlink=1, st_uid=0, st_gid=0, st_size=413, st_atime=1523480272, st_mtime=1539787740, st_ctime=1523480272)
For more information about what os.stat_result
contains, refer to the documentation. For the modification time you want fname.stat().st_mtime
:
>>> import datetime
>>> mtime = datetime.datetime.fromtimestamp(fname.stat().st_mtime, tz=datetime.timezone.utc)
>>> print(mtime)
datetime.datetime(2018, 10, 17, 10, 49, 0, 249980)
If you want the creation time on Windows, or the most recent metadata change on Unix, you would use fname.stat().st_ctime
:
>>> ctime = datetime.datetime.fromtimestamp(fname.stat().st_ctime, tz=datetime.timezone.utc)
>>> print(ctime)
datetime.datetime(2018, 4, 11, 16, 57, 52, 151953)
This article has more helpful info and examples for the pathlib module.
datetime.datetime.fromtimestamp(t, tz=datetime.timezone.utc)
here, as the naive datetime
object returned otherwise has a tendency to be interpreted as being in the local timezone instead while Unix timestamps are always relative to the 01.01.1970 00:00 UTC. –
Fatwitted Getting some sort of modification date in a cross-platform way is easy - just call os.path.getmtime(path)
and you'll get the Unix timestamp of when the file at path
was last modified.
Getting file creation dates, on the other hand, is fiddly and platform-dependent, differing even between the three big OSes:
- On Windows, a file's
ctime
(documented at https://msdn.microsoft.com/en-us/library/14h5k7ff.aspx) stores its creation date. You can access this in Python throughos.path.getctime()
or the.st_ctime
attribute of the result of a call toos.stat()
. This won't work on Unix, where thectime
is the last time that the file's attributes or content were changed. - On Mac, as well as some other Unix-based OSes, you can use the
.st_birthtime
attribute of the result of a call toos.stat()
. On Linux, this is currently impossible, at least without writing a C extension for Python. Although some file systems commonly used with Linux do store creation dates (for example,
ext4
stores them inst_crtime
) , the Linux kernel offers no way of accessing them; in particular, the structs it returns fromstat()
calls in C, as of the latest kernel version, don't contain any creation date fields. You can also see that the identifierst_crtime
doesn't currently feature anywhere in the Python source. At least if you're onext4
, the data is attached to the inodes in the file system, but there's no convenient way of accessing it.The next-best thing on Linux is to access the file's
mtime
, through eitheros.path.getmtime()
or the.st_mtime
attribute of anos.stat()
result. This will give you the last time the file's content was modified, which may be adequate for some use cases.
Putting this all together, cross-platform code should look something like this...
import os
import platform
def creation_date(path_to_file):
"""
Try to get the date that a file was created, falling back to when it was
last modified if that isn't possible.
See https://mcmap.net/q/45386/-how-do-i-get-file-creation-and-modification-date-times for explanation.
"""
if platform.system() == 'Windows':
return os.path.getctime(path_to_file)
else:
stat = os.stat(path_to_file)
try:
return stat.st_birthtime
except AttributeError:
# We're probably on Linux. No easy way to get creation dates here,
# so we'll settle for when its content was last modified.
return stat.st_mtime
ext4
drives under Linux, and I'd like to learn what happens when Linux reads files written by Windows, or vica versa, given that they use st_ctime
differently. –
Haematinic "w"
, it's not replacing it, it just opens the existing file and truncates it. Even though the file contents are completely unrelated to whatever it had on creation, you'd still be told the file was "created" well before the current version. Conversely, editors that use atomic replace on save (original file is replaced by new work-in-progress temp file) would show a more recent creation date, even if you just deleted one character. Use the modification time, don't grub for creation time. –
Aircrew stat.st_ctime
is more pertinent because, in many cases, the time of last metadata change can be the creation time (at least ctime
is closer to the real creation time than mtime
). Therefore, you could simply replace your snippet by stat = os.stat(path_to_file); try: return stat.st_birthtime; except AttributeError: return stat.st_ctime
. What do you think? Cheers –
Methane ctime
should always be equal to or later than mtime
, because an mtime
change causes a ctime
change (because the mtime
itself is considered "metadata"). See https://mcmap.net/q/46455/-is-ctime-always-lt-mtime where I provide some example code to illustrate this. –
Haematinic st_ctime
comes up, we need these ugly wart disclaimers about the different meaning in Windows, and this makes for ugly code even if the ctime
is rarely used. I'd like to change the Windows implementation of os.stat
to support st_birthtime
in addition to st_ctime
, but deprecate using st_ctime
as the "creation time". An environment variable would allow using st_ctime
as the change time (supported by NTFS; we'd have to modify the queries used to implement os.stat
), which would subsequently be made the default in the next version. –
Openwork statx
system call does allow querying btime
if available for the given file. (This is not used/exposed even in Python 3.8 however.) –
Fatwitted int(subprocess.check_output(["stat", "--printf", "%W", path_to_file]))
to get the creation/birth date in linux. –
Impeller st_ctime
updates for Windows from Python 3.12: docs.python.org/3.12/library/os.html#os.stat_result.st_ctime –
Prelacy You have a couple of choices. For one, you can use the os.path.getmtime
and os.path.getctime
functions:
import os.path, time
print("last modified: %s" % time.ctime(os.path.getmtime(file)))
print("created: %s" % time.ctime(os.path.getctime(file)))
Your other option is to use os.stat
:
import os, time
(mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(file)
print("last modified: %s" % time.ctime(mtime))
Note: ctime()
does not refer to creation time on *nix systems, but rather the last time the inode data changed. (Thanks to kojiro for making that fact more clear in the comments by providing a link to an interesting blog post.)
ctime
gets updated whenever the mtime
does (since the mtime
is "metadata"), and so the ctime
is normally always equal to or ahead of the mtime
. Treating ctime
as "created" time thus makes no sense at all. -1! –
Haematinic Last modified: Fri Jan 31 11:08:13 2020
and Created: Fri Jan 31 11:08:13 2020
on Linux Ubuntu 16.04! –
Tetzel time.ctime(os.path.getmtime(file))
returns 2 types of strings, depending if the file has been modified by the system or by the user. If it has been modified by the system the string will have 2 spaces between the month and the day. I don't know why –
Wheedle The best function to use for this is os.path.getmtime(). Internally, this just uses os.stat(filename).st_mtime
.
The datetime module is the best for manipulating timestamps, so you can get the modification date as a datetime
object like this:
import os
import datetime
def modification_date(filename):
t = os.path.getmtime(filename)
return datetime.datetime.fromtimestamp(t)
Usage example:
>>> d = modification_date('/var/log/syslog')
>>> print d
2009-10-06 10:50:01
>>> print repr(d)
datetime.datetime(2009, 10, 6, 10, 50, 1)
getmtime
is the nearest thing available on Unix (where getting creation dates isn't possible), but is definitely not the best function to use on Windows, where the ctime
is a creation time. –
Haematinic datetime.datetime.fromtimestamp(t, tz=datetime.timezone.utc)
here, as the naive datetime
object returned otherwise has a tendency to be interpreted as being in the local timezone instead while Unix timestamps are always relative to the 01.01.1970 00:00 UTC. –
Fatwitted In Python 3.4 and above, you can use the object oriented pathlib module interface which includes wrappers for much of the os module. Here is an example of getting the file stats.
>>> import pathlib
>>> fname = pathlib.Path('test.py')
>>> assert fname.exists(), f'No such file: {fname}' # check that the file exists
>>> print(fname.stat())
os.stat_result(st_mode=33206, st_ino=5066549581564298, st_dev=573948050, st_nlink=1, st_uid=0, st_gid=0, st_size=413, st_atime=1523480272, st_mtime=1539787740, st_ctime=1523480272)
For more information about what os.stat_result
contains, refer to the documentation. For the modification time you want fname.stat().st_mtime
:
>>> import datetime
>>> mtime = datetime.datetime.fromtimestamp(fname.stat().st_mtime, tz=datetime.timezone.utc)
>>> print(mtime)
datetime.datetime(2018, 10, 17, 10, 49, 0, 249980)
If you want the creation time on Windows, or the most recent metadata change on Unix, you would use fname.stat().st_ctime
:
>>> ctime = datetime.datetime.fromtimestamp(fname.stat().st_ctime, tz=datetime.timezone.utc)
>>> print(ctime)
datetime.datetime(2018, 4, 11, 16, 57, 52, 151953)
This article has more helpful info and examples for the pathlib module.
datetime.datetime.fromtimestamp(t, tz=datetime.timezone.utc)
here, as the naive datetime
object returned otherwise has a tendency to be interpreted as being in the local timezone instead while Unix timestamps are always relative to the 01.01.1970 00:00 UTC. –
Fatwitted import os, time, datetime
file = "somefile.txt"
print(file)
print("Modified")
print(os.stat(file)[-2])
print(os.stat(file).st_mtime)
print(os.path.getmtime(file))
print()
print("Created")
print(os.stat(file)[-1])
print(os.stat(file).st_ctime)
print(os.path.getctime(file))
print()
modified = os.path.getmtime(file)
print("Date modified: "+time.ctime(modified))
print("Date modified:",datetime.datetime.fromtimestamp(modified))
year,month,day,hour,minute,second=time.localtime(modified)[:-3]
print("Date modified: %02d/%02d/%d %02d:%02d:%02d"%(day,month,year,hour,minute,second))
print()
created = os.path.getctime(file)
print("Date created: "+time.ctime(created))
print("Date created:",datetime.datetime.fromtimestamp(created))
year,month,day,hour,minute,second=time.localtime(created)[:-3]
print("Date created: %02d/%02d/%d %02d:%02d:%02d"%(day,month,year,hour,minute,second))
prints
somefile.txt
Modified
1429613446
1429613446.0
1429613446.0
Created
1517491049
1517491049.28306
1517491049.28306
Date modified: Tue Apr 21 11:50:46 2015
Date modified: 2015-04-21 11:50:46
Date modified: 21/04/2015 11:50:46
Date created: Thu Feb 1 13:17:29 2018
Date created: 2018-02-01 13:17:29.283060
Date created: 01/02/2018 13:17:29
Note: A file's ctime on Linux is slightly different than on Windows.
Windows users know theirs as "creation time".
Linux users know theirs as "change time".
ctime <= mtime
always holds while, in complete opposition, on Unix mtime <= ctime
always holds. Your answer suggests that ctime is the “Date created” of the file, without any suggestion that this doesn’t portably hold at all. Had you called it “Date created (on Windows)” or said “This answer only applies to Windows” at the top it would be very different, but that’s not what you are doing event after your (still appreciated) update of your answer. –
Fatwitted os.path.getctime(file)
in python implemented it wrong on linux? That's who you might as well be arguing with. And if something ain't possible on linux/windows, you clearly wouldn't. Otherwise please tell me how we get linux's "change time" on windows. –
Whitley ctime
in the stat results whose meaning is entirely different depending on platform, quoting straight from the docs of os.path.getctime
: Return the system’s ctime which, on some systems (like Unix) is the time of the last metadata change, and, on others (like Windows), is the creation time for path. –
Fatwitted In newer code you should probably use os.path.getmtime() (thanks, Christian Oudard).
But note that it returns a floating point value of time_t with fraction seconds (if your OS supports it).
os.path.getmtime()
has been around since Python 1.5.2 (see the old docs), released before I'd lost most of my baby teeth and almost a decade before you wrote the original version of this answer. –
Haematinic There are two methods to get the mod time, os.path.getmtime() or os.stat(), but the ctime is not reliable cross-platform (see below).
os.path.getmtime()
getmtime(path)
Return the time of last modification of path. The return value is a number giving the
number of seconds since the epoch (see the time module). Raise os.error if the file does
not exist or is inaccessible. New in version 1.5.2. Changed in version 2.3: If
os.stat_float_times() returns True, the result is a floating point number.
os.stat()
stat(path)
Perform a stat() system call on the given path. The return value is an object whose
attributes correspond to the members of the stat structure, namely: st_mode (protection
bits), st_ino (inode number), st_dev (device), st_nlink (number of hard links), st_uid
(user ID of owner), st_gid (group ID of owner), st_size (size of file, in bytes),
st_atime (time of most recent access), st_mtime (time of most recent content
modification), st_ctime (platform dependent; time of most recent metadata change on Unix, or the time of creation on Windows):
>>> import os
>>> statinfo = os.stat('somefile.txt')
>>> statinfo
(33188, 422511L, 769L, 1, 1032, 100, 926L, 1105022698,1105022732, 1105022732)
>>> statinfo.st_size
926L
>>>
In the above example you would use statinfo.st_mtime or statinfo.st_ctime to get the mtime and ctime, respectively.
os.stat
returns a named tuple with st_mtime
and st_ctime
attributes. The modification time is st_mtime
on both platforms; unfortunately, on Windows, ctime
means "creation time", whereas on POSIX it means "change time". I'm not aware of any way to get the creation time on POSIX platforms.
dir(..)
on one. E.g. dir(os.stat(os.listdir('.')[0]))
–
Pithecanthropus It may worth taking a look at the crtime
library which implements cross-platform access to the file creation time.
from crtime import get_crtimes_in_dir
for fname, date in get_crtimes_in_dir(".", raise_on_error=True, as_epoch=False):
print(fname, date)
# file_a.py Mon Mar 18 20:51:18 CET 2019
debugfs
on Linux which is by definition unstable, requires top-level root access for everything and in pretty much every aspect tends to be one of the things your mother always warned you about. (But yes, it probably works if you're really desperate and happen to be the real superuser on a system without secure boot…) –
Fatwitted I am a fan of pathlib.
from pathlib import Path
target = Path('out/soong/build.ninja')
mtime = target.stat().st_mtime
atime = target.stat().st_atime
ctime = target.stat().st_ctime
I believe this is also feasible for Windows Subsystem for Linux (WSL).
pathlib.Path
object, it automatically represents the OS specific implementation. And since these implementations inherit from pathlib.Path
, the function/properties you list are available on on any OS, not only Windows or the WSL. –
Lamartine >>> import os
>>> os.stat('feedparser.py').st_mtime
1136961142.0
>>> os.stat('feedparser.py').st_ctime
1222664012.233
>>>
If the following symbolic links are not important, you can also use the os.lstat
builtin.
>>> os.lstat("2048.py")
posix.stat_result(st_mode=33188, st_ino=4172202, st_dev=16777218L, st_nlink=1, st_uid=501, st_gid=20, st_size=2078, st_atime=1423378041, st_mtime=1423377552, st_ctime=1423377553)
>>> os.lstat("2048.py").st_atime
1423378041.0
os.stat
does include the creation time. There's just no definition of st_anything for the element of os.stat()
that contains the time.
So try this:
os.stat('feedparser.py')[8]
Compare that with your create date on the file in ls -lah
They should be the same.
© 2022 - 2024 — McMap. All rights reserved.
pathlib
instead ofos
, see @StevenC.Howell's answer. Perhaps one could even change the accepted answer to thepathlib
answer? – Emmettemmeypathlib
is wrappingos
, and then ask what's the point in usingpathlib
then? The answer, as I understand it, is thatpathlib
has a smaller namespace thanos
and generally results in cleaner code. – Inextricableos
and wasted time and nerves, therefore the comment. You can find more ofpathlib
vs.os
with a quick internet search. – Emmettemmey