What's the best cross-platform way to get file creation and modification dates/times, that works on both Linux and Windows?
-
71You can't get file creation time in a cross-platform way. See http://docs.python.org/library/os.path.html#os.path.getctime – Glyph Jul 19 '10 at 17:13
-
6Mind that the accepted answer is not recommended anymore, use `pathlib` instead of `os`, see @StevenC.Howell's answer. Perhaps one could even change the accepted answer to the `pathlib` answer? – questionto42 Nov 30 '20 at 16:25
-
@questionto42, while I agree, it might pay to give a reason why you advise that. Just from looking at this SO post, many would see (from @StephenCHowell's answer) that `pathlib` is wrapping `os`, and then ask what's the point in using `pathlib` then? The answer, as I understand it, is that `pathlib` has a smaller namespace than `os` and generally results in cleaner code. – Jlanger Jan 13 '22 at 21:55
-
1@Jlanger I wrote this because I had awkward code with os for complex recursions + path checking + file name replacing over a larger file system until I found pathlib. Pathlib is easier and cleaner - I even remember it was more powerful due to its Path objects, thus, more pythonic. Smaller namespace is not the point. It is dangerous if a strongly used Q/A like this spreads older ways of programming, I took `os` and wasted time and nerves, therefore the comment. You can find more of `pathlib` vs. `os` with a quick internet search. – questionto42 Jan 13 '22 at 22:12
-
1@questionto42 totally agreed, I'm aware of the differences and I was trying to be brief, you clarified it better than I did. – Jlanger Jan 14 '22 at 00:29
13 Answers
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 http://stackoverflow.com/a/39501288/1709587 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

- 3,543
- 1
- 18
- 23

- 143,130
- 81
- 406
- 459
-
20I've done my best to throw this together (and spent a few hours researching in the process), and I'm sure it's at least *more correct* than the answers that were here previously, but this is a *really hard* topic and I'd appreciate any corrections, clarifications, or other input that people can offer. In particular, I'd like to construct a way of accessing this data on `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. – Mark Amery Sep 14 '16 at 23:59
-
40Frankly, file creation time is usually fairly useless. When you open an existing file for write with mode `"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. – ShadowRanger Oct 17 '16 at 14:00
-
4After many years, I've finally found a use for file creation time! I'm writing code to check a file naming convention in certain directories, so first of all I want to consider files that were first named after the convention was instituted. Replacing the entire contents (mtime) is irrelevant: if it was already there then it's grandfathered in. – Steve Jessop Dec 03 '16 at 15:01
-
2Hi Mark. I propose a simplification. On Linux, returning [`stat.st_ctime`](https://docs.python.org/3/library/os.html?highlight=st_ctime#os.stat_result.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 – oHo Aug 07 '17 at 15:31
-
5@olibre *"at least ctime is closer to the real creation time than mtime"* - no it isn't; this is something I've seen stated several times but it's totally false. Unless you've manually messed with the values in your inode, `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://stackoverflow.com/a/39521489/1709587 where I provide some example code to illustrate this. – Mark Amery Aug 09 '17 at 11:08
-
1Whenever `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. – Eryk Sun Jul 10 '18 at 16:10
-
If the files are images, you can also get try to get the taken date usin PIL: ` from PIL import Image def get_date_taken(path): try: return Image.open(path)._getexif()[36867] except: return os.path.getmtime(path) ` – Fernando Wittmann Nov 22 '18 at 22:57
-
4Note that the statement “Although some file systems commonly used with Linux do store creation dates […], the Linux kernel offers no way of accessing them” is not true anymore since Linux 4.11: The `statx` system call *does* allow querying `btime` if available for the given file. (This is not used/exposed even in Python 3.8 however.) – ntninja Jan 05 '20 at 19:05
-
One could use something like `int(subprocess.check_output(["stat", "--printf", "%W", path_to_file]))` to get the creation/birth date in linux. – Wups Jan 06 '22 at 13:18
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.)

- 30,738
- 21
- 105
- 131

- 370,779
- 53
- 539
- 685
-
190Just in case anyone misses @Glyph's comment to the question, [ctime does not mean creation time on POSIX systems](http://userprimary.net/posts/2007/11/18/ctime-in-unix-means-last-change-time-not-create-time/). I wonder how many people have skimmed this post over the past three years and gone on to write buggy code. – kojiro Nov 07 '11 at 20:37
-
16Keep in mind the first example gives you a string, not a datetime or number. – gak Jul 12 '13 at 00:53
-
1@kojiro the blog post you've linked to could be more explicit that on Unix a file's `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! – Mark Amery Sep 12 '16 at 20:15
-
Your first option returns the same results for both file creation and last modification! `Last modified: Fri Jan 31 11:08:13 2020` and `Created: Fri Jan 31 11:08:13 2020` on Linux Ubuntu 16.04! – Farid Alijani Jan 31 '20 at 09:12
-
1I discover that `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 – Matteo Antolini Apr 10 '20 at 11:59
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)

- 30,738
- 21
- 105
- 131

- 48,140
- 25
- 66
- 69
-
2This answer is also a little bit wrong. `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. – Mark Amery Sep 12 '16 at 20:00
-
6@MarkAmery - This answer is clearly labeled as just being about modification time. – ArtOfWarfare Jan 19 '17 at 18:30
-
3I strongly recommend using `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. – ntninja Jan 26 '22 at 18:29
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.

- 16,902
- 15
- 72
- 97
-
1I strongly recommend, as also mentioned with the previous anwser, using `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. – ntninja Jan 26 '22 at 18:32
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".

- 2,993
- 1
- 19
- 32
-
1@ntninja are you sure about that? i only use Windows and this absolutely works. i wrote this script in early 2015. i find it was more clear, straight to the point, complete and self explanatory than others here. (which i happened to decide to look up here instead of my old scripts just incase there was anything new. nope... this is the way) – Puddle Jan 09 '20 at 14:46
-
1Oh, I meant to say “… this will not give you the file's creation time, **unless you are** on Windows”. Sorry! Fact remains that this answer is not portable and doesn't mention this fact. (Example output on Linux: https://pastebin.com/50r5vGBE ) – ntninja Jan 09 '20 at 20:20
-
1Already left some other comments here and I'll post an answer that works on (recent) Linux as well soon. But really, the only thing wrong in your post is that it's Windows-only answer that doesn't mention this fact. In the question OP even specifically asked for a Windows and Linux compatible solution. As such I think it would be very helpful if you added this “detail” somewhere at the top, so that people aren't mislead into thinking ctime is what they're looking when targeting multiple platforms. – ntninja Jan 11 '20 at 16:43
-
@ntninja I just realized you were wrong to blame me (and others) over ctime. What you really meant to point out is that a file's ctime on linux is slightly different than on windows. But if you're programming for linux users you wouldn't be trying to provide info from a windows perspective anyway. Same way you wouldn't try provide the linux equivalent to windows users. That's like complaining you can't open cmd on linux, that it opens its version of it. lol (And surely there's a reason such crossplatform function even exists) But besides that, mtime is what's most significant to anyone anyway. – Puddle Jan 25 '22 at 09:07
-
It isn’t “slightly different” on Windows vs Unix – it’s **completely different**: Unless you manually update the values, on Windows `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. – ntninja Jan 26 '22 at 17:55
-
Also your ad hominem statement “*if you're programming for linux users you wouldn't be trying to provide info from a windows perspective anyway*” is completely false as I, assuming I am aware of respective issue, would definitely point this out in code review and teaching if there is ever any chance that the resulting may be run cross-platform (i.e. just about always).
After all, the person in question may have would up on StackOverflow after searching for “file creation time” and gone “oh the C in ctime is for *creation*” after quickly skimming over your answer. – ntninja Jan 26 '22 at 18:03 -
Considering the question explicitly states it has to work on multiple OS, it seems important to keep those details correct. – Mast Jan 26 '22 at 18:32
-
@ntninja So are we saying the person who implemented `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. – Puddle Jan 27 '22 at 03:18
-
@Mast I agree it's worth pointing out. (hence i edited my answer) But one half of the answer is still correct. Before i thought he was telling me my whole answer was wrong. – Puddle Jan 27 '22 at 03:20
-
@Puddle: No they did not, it’s just an unfortunate fact that we have a field named `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**.* – ntninja Jan 27 '22 at 13:38
-
@ntninja If there's a cross platform solution, then it makes no sense the function is inconsistant. If there isn't a cross platform solution (explaining the reasonable/expected alternative), then that's the answer. You just can't. Just like the very first reply to the question answered. "You can't get file creation time in a cross-platform way. See [os.path.getctime](https://docs.python.org/3/library/os.path.html#os.path.getctime)" – Puddle Jan 27 '22 at 14:05
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).

- 30,738
- 21
- 105
- 131

- 94,801
- 28
- 188
- 263
-
48
-
11The "in newer code" clause here is a bit misleading. `os.path.getmtime()` has been around since Python 1.5.2 (see the [old docs](https://docs.python.org/release/1.5.2/lib/module-os.path.html)), released before I'd lost most of my baby teeth and almost a decade before you wrote the original version of this answer. – Mark Amery Sep 12 '16 at 19:04
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.

- 32,208
- 39
- 178
- 361

- 1,630
- 9
- 27
-
Here is more about tagged-tuples: http://stackoverflow.com/questions/2970608/what-are-named-tuples-in-python They work like tuples, but try `dir(..)` on one. E.g. `dir(os.stat(os.listdir('.')[0]))` – Evgeni Sergeev Oct 16 '13 at 09:20
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

- 18,571
- 11
- 90
- 141
-
3I strongly advise against this: It uses `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…) – ntninja Jan 08 '20 at 00:00
-
@ntninja I would probably never use in in production neither, but it may be useful for "home scripting". – Delgan Jan 08 '20 at 08:04
-
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).

- 41
- 2
-
It is my understanding that when you instantiate a `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. – FlorianK Jul 28 '23 at 16:37
>>> import os
>>> os.stat('feedparser.py').st_mtime
1136961142.0
>>> os.stat('feedparser.py').st_ctime
1222664012.233
>>>

- 33,530
- 16
- 61
- 61
-
1-1: As mentioned elsewhere this will not give you the file's creation time, unless you are on Windows (which the answer does not even mention!). – ntninja Jan 08 '20 at 00:03
-
An explanation would be in order. E.g., what do we see? What do the results tell us? What is the conclusion? On which platform was this tested? What version of Python and of libraries? Can you link to documentation for the used properties? – Peter Mortensen Jan 27 '22 at 20:40
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

- 30,738
- 21
- 105
- 131

- 8,177
- 11
- 44
- 54
-
1This will give the time of last read (at least on Unix), which *definitely* isn't what was asked for. – Mark Amery Sep 12 '16 at 20:16
-
What do you mean by *"...symbolic links are not important"*? Can you elaborate? – Peter Mortensen Jan 27 '22 at 20:43
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.

- 52,748
- 3
- 38
- 55
-
8Wrong! os.stat('feedparser.py')[8] refers to st_mtime, not creation time. Please refer to the documentation: http://docs.python.org/library/os.html#os.stat – millerdev Jun 10 '11 at 22:20
-
5