11

When using os.stat() in Python, can I assume that st_ctime is always less then or equal to st_mtime? If not, why not?

The code will always run on Linux, but if there is a difference between OSes, that would be good to know.

Mark Amery
  • 143,130
  • 81
  • 406
  • 459
Peter Smit
  • 27,696
  • 33
  • 111
  • 170
  • 2
    This question was a bit confused, and its accepted answer is really really wrong. Normally `mtime <= ctime`, not the other way around! See [my answer](http://stackoverflow.com/a/39521489/1709587) for explanation and a demonstration of this at the shell. – Mark Amery Sep 15 '16 at 22:57
  • 2
    `ctime` is not creation at all, it's metadata **change** time. The accepted answer is thus entirely and completely wrong: it's not just "some situations" or (otherwise, as that phrase implies) rare corner-cases where this assumption is invalid! – Charles Duffy Sep 15 '16 at 23:05

5 Answers5

8

You've got this the wrong way round! On Linux (or Mac, or any other Unix system), ctime will normally always be LATER than mtime, not earlier. Unlike Windows, where ctime is a file creation date and mtime is a file modification date, in Unix they're both modification dates, with the difference being that:

  • mtime gets updated whenever the file content changes
  • ctime gets updated whenever the file attributes change, including its mtime

The man page for (at least some variants of) the stat utility refers to these respectively as the "Time of last data modification" and "Time of last status change".

Thus whenever mtime gets updated, ctime also gets updated. The only mechanisms I know of by which you could ever get an mtime that is greater than the ctime are:

Since you've asked in the context of Python, let's make a simple Python tool that outputs the mtime and ctime of a given file to help us demonstrate this. We'll use the convenient os.path.getctime and os.path.getctime APIs in our script, but using the st_ctime and st_mtime properties of the result of a stat call would give exactly the same results:

#!/usr/bin/python
import os
import sys

target_filename = sys.argv[1]

mtime = os.path.getmtime(target_filename)
ctime = os.path.getctime(target_filename)

print('mtime: %f ctime: %f' % (mtime, ctime))

We can save that as pystat.py, make it executable, and experiment at our Unix shell:

$ # Times are initially equal:
$ touch foo
$ ./pystat.py foo
mtime: 1473979539.786961 ctime: 1473979539.786961
$ 
$ # It doesn't matter how I create the file:
$ echo qwerty > bar
$ ./pystat.py bar
mtime: 1473979561.218961 ctime: 1473979561.218961
$ 
$ # 'touch'ing a created file updates both times:
$ touch foo
$ ./pystat.py foo
mtime: 1473979584.642960 ctime: 1473979584.642960
$ 
$ touch bar
$ ./pystat.py bar
mtime: 1473979592.762960 ctime: 1473979592.762960
$ 
$ # Modifying an existing file updates both times:
$ echo stuff >> foo
$ ./pystat.py foo
mtime: 1473979622.722959 ctime: 1473979622.722959
$ 
$ # Changing permissions ONLY updates the ctime:
$ chmod 777 foo
$ ./pystat.py foo
mtime: 1473979622.722959 ctime: 1473979643.542958
$ 
$ # So does moving a file:
$ mv bar baz
$ ./pystat.py baz
mtime: 1473979592.762960 ctime: 1473979659.586958
$ 
$ # Consequently, both files now have ctime > mtime
$
$ # However, we CAN manually set mtime in the future
$ # and thereby cause ctime < mtime:
$ touch --date="2041-01-01 12:34:56" foo
$ ./pystat.py foo
mtime: 2240656496.000000 ctime: 1473989678.258937
Community
  • 1
  • 1
Mark Amery
  • 143,130
  • 81
  • 406
  • 459
  • Perfect answer! For me only thing is missing (even though it has not been asked), is the link to the bash's "stat" function, which can give you the "birth" date, or the "real" creation time. According to my research the os.stat function in Python does not serve this info but in bash it does (if file system supports this like my ext4). So one workaround would be to call this bash function via python and parse the text results ...That would of course imply OS and file system dependent code...hmmm..any other suggestions? – pedda Apr 10 '23 at 18:38
5

Please define "less than", do you mean newer or older? You can't assume that ctime happened before mtime, but normally ctime is the same as, or after, mtime.

ctime on unix is not "create time", but "change time". mtime is updated when a file's contents are changed, but ctime is updated when file metadata is changed (which means it's updated when mtime is updated too), so it's perfectly normal for ctime to be after mtime. Here's an example:

user@ubuntu:~$ touch test
user@ubuntu:~$ chmod 600 test
user@ubuntu:~$ stat test
  File: «test»
  Size: 0          Blocks: 0          IO Block: 4096   regular empty file
Device: 700h/1792d Inode: 222375      Links: 1
Access: (0600/-rw-------)  Uid: ( 1000/    user)   Gid: ( 1000/    user)
Access: 2011-01-03 12:35:15.945973569 +0100
Modify: 2011-01-03 12:35:15.945973569 +0100
Change: 2011-01-03 12:35:24.024998291 +0100

Also, I believe that on Windows, the ctime field actually does mean "create time", and that this is an os difference between Windows and Unix. I've read something about this online, but I'll let you do your own research on this.

ketil
  • 51
  • 1
4

It's entirely possible to set both values programmatically. You might also be able to have this happen "naturally" by setting the clock back to before the file was created.

In other words, yes. It would be unusual but is entirely possible.

Steven Schlansker
  • 37,580
  • 14
  • 81
  • 100
  • I know of no way to set the ctime on Unix programmatically. I do not know about Windows. Regardless, could you please provide a reference to the API(s) you are thinking of? – Nemo Jul 07 '11 at 00:30
4

There are some situations in which this assumption could prove invalid (and would depend very much on the OS implementation):

  • Timezones. If you create a file in, say, UTC+4, and then modify it when the current timezone is UTC-8, and the operating system doesn't use UTC for all timestamps behind-the-scenes, the modified time will be less than the created time. It would be surprising for a modern operating system (Windows, OSX, one of the BSDs or Linux) to have mtime < ctime in this case.
  • Resetting the OS time. This could affect the modified time to create this situation. I would say it would be much more likely that you would have mtime < ctime without complaint from the OS in this case, assuming there aren't checks in place in the filesystem driver to avoid this case.
  • Modifying the times through system calls. Again, the filesystem driver may have checks in place to avoid unusual situations such as this.

Both of these are reproducable: your best bet is to take a variety of operating systems that you plan on targeting and testing this behaviour. All I can provide is speculation.

Also, st_ctime is not necessarily the "created time", but rather the time of the "last status change" (source). utime marks the ctime of the file to be updated (source) and it's parameter of type "utimbuf" has no member for ctime. So it is technically possible for ctime to be a time past mtime if the operating system and filesystem permit it. The os.stat documentation actually mentions this:

platform dependent; time of most recent metadata change on Unix, or the time of creation on Windows

Whilst Python hides a lot of the C side of things, os.stat and friends are all built upon the same base C system calls so the specification for them is a great place to look for more information.

Matthew Iselin
  • 10,400
  • 4
  • 51
  • 62
  • I think you got it backwards in the first sentence, right? :-) – Lennart Regebro Jan 03 '11 at 12:01
  • 1
    "some situations" is very, very cautious language for an assumption that has no significant basis (and is generally *the opposite* of the actual situation, wherein ctime >= mtime). – Charles Duffy Sep 15 '16 at 23:05
  • 1
    As @CharlesDuffy says, this answer is wrong and, really, a bit silly. Looking at the revisions, I would guess that you wrote revision 1, then realised that `ctime` != "creation time", and edited a proviso to that effect into your answer (creating revision 2). The trouble is that you (and those who have upvoted you) seem not to have noticed that `ctime` not being a creation time *totally invalidates* the overall thrust of this answer; you rather effectively torpedo your own argument in the paragraph starting *'Also, st_ctime is not necessarily the "created time"...'* – Mark Amery Sep 15 '16 at 23:14
0

As @ketil says, ctime is updated when file metadata is changed.

One way this can change is that if you move a file from one directory to another. ctime will change, but not mtime.

touch test_file
mv test_file another_directory/
stat another_directory/test_file 

gives

  File: `another_directory/test_file'
  Size: 0           Blocks: 0          IO Block: 4096   regular empty file
Device: 80ah/2058d  Inode: 23183108    Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1004/  agrimm)   Gid: ( 1004/  agrimm)
Access: 2011-07-07 10:11:27.000000000 +1000
Modify: 2011-07-07 10:11:27.000000000 +1000
Change: 2011-07-07 10:11:43.000000000 +1000
Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338