Get file mtime with millisecond resolution from Java
Asked Answered
M

5

15

When I read the mtime of a file from Java using Files.getLastModifiedTime, the return value is truncated to whole seconds. I know this works on other systems to get mtimes with millisecond resolution, so what could be different about mine?

Here is a complete standalone test which compiles and runs:

import java.nio.file.attribute.FileTime;
import java.nio.file.Files;
import java.nio.file.Paths;
public class Test {
  public static void main(String[] args) throws java.io.IOException {
    FileTime timestamp = Files.getLastModifiedTime(Paths.get("/tmp/test"));
    System.out.println(timestamp.toMillis());
  }
}

The output is (with my particular test file) 1405602038000, whereas ls shows:

$ ls --full-time /tmp/test                                                                                                                                                                                                    
-rw-rw-r-- 1 daniel daniel 0 2014-07-17 16:00:38.413008992 +0300 /tmp/test

I would expect the Java output to be 1405602038413.

I'm running on Linux with ext4. I tried both openjdk 1.7 and Oracle jdk 1.8.

Mailbag answered 17/7, 2014 at 13:14 Comment(0)
L
13

The ability to get file timestamps on *nix systems with higher precision was added in Java 8 by this commit, however, on the native side, it requires POSIX 2008 compliance:

#if (_POSIX_C_SOURCE >= 200809L) || defined(__solaris__)
    (*env)->SetLongField(env, attrs, attrs_st_atime_nsec, (jlong)buf->st_atim.tv_nsec);
    (*env)->SetLongField(env, attrs, attrs_st_mtime_nsec, (jlong)buf->st_mtim.tv_nsec);
    (*env)->SetLongField(env, attrs, attrs_st_ctime_nsec, (jlong)buf->st_ctim.tv_nsec);
#endif

And, apparently, the Java build you are using doesn't set it, so nanoseconds part of the timestamp is not available and stays zero.

Louella answered 17/7, 2014 at 14:18 Comment(1)
In case anyone is interested, the bug is registered here: bugs.java.com/bugdatabase/view_bug.do?bug_id=8177809Ashlieashlin
G
4

I looked at the source code:

In the Java 7 case, the method gives the last modified timestamp with 1 second precision:

In the Java 8 case, it looks like it should give microsecond precision:

But in either case, the code doesn't seem to provide a way to get the timestamp with a different precision.

Gaucho answered 17/7, 2014 at 13:57 Comment(5)
This #if seems to be related. I am able to reproduce the problem with Oracle Java 8 on Ubuntu.Louella
When I look with a debugger at the UnixFileAttributes.st_mtime, it's already in whole seconds. Something is wrong on the native side. Also, I can't get millis even when using in Java 8 (the oracle build, not openjdk). Can you actually get millis when you run java 8?Mailbag
@Louella bingo! I'm on ubuntu too. Now we just need to know why Ubuntu builds (and the Oracle build!) don't set POSIX2008 compliance. Do builds for other OSs or distros set it? What are the people running for whom this code works? If you make this into an answer I'll accept it, and thanks!Mailbag
Current ubuntu jdk8 indeed gives microsecond precision. But they could up it to nanosecond precision, since the moment in the comment "We can re-visit this if FileTime is updated to define a from(secs,nsecs) method." has come. FileTime defines a from(Instant) method since 1.8.Velarium
But that is all moot unless the file system stores timestamps with high precision. The sources that I have seen say that timestamp precision is highly variable across differen file systems. (Then there is clock accuracy to consider.)Gaucho
C
4

It seems to be fixed in java 9

  • Oracle jdk 9.0.4 - Files.getLastModifiedTime gives millisecond resolution
  • Oracle jdk 1.8.162 - Files.getLastModifiedTime gives second resolution
Caboodle answered 26/2, 2018 at 21:47 Comment(1)
Also fixed in Debian’s 1.8.0_292Godparent
N
0

You can access the nano seconds using toInstant().getNano()

BasicFileAttributes attr = Files.readAttributes(file.toPath(), BasicFileAttributes.class);
FileTime ts = attr.lastModifiedTime();
System.out.println("Nano: " + ts.toInstant().getNano());

Yields:

Nano: 412413961

# Actual ls ouput:
-rw-r--r-- 1 jotschi jotschi 822 2022-12-25 18:33:12.412413961 +0100 pom.xml
Nichani answered 25/12, 2022 at 17:39 Comment(0)
P
-2

You can use the simple date format to display time is milliseconds:

java.nio.file.attribute.FileTime time = java.nio.file.Files.getLastModifiedTime(java.nio.file.Paths.get("/tmp/test"))
java.text.SimpleDateFormat dateformat = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
System.out.println(dateformat.format(time.toMillis()));
Pugging answered 17/7, 2014 at 13:30 Comment(3)
You misunderstand. The value returned by time.toMillis is already truncated to whole-second resolution (i.e. always ends in three zeroes). That's what's wrong.Mailbag
That seems strange, because when I try it on my computer, I do get millisecond resolution.Pugging
I'm aware that it works for others (I edited the question to clarify that), but it doesn't work here and I'm trying to figure out why.Mailbag

© 2022 - 2024 — McMap. All rights reserved.