With the following code
long newDate = Date.parse("12 Oct 1960 13:25:00");
file.setLastModified(newDate);
I get java.lang.IllegalArgumentException: Negative time
And with this code
public class FileTimes {
static long getCalendarMillis(int year) {
Calendar cal = Calendar.getInstance();
cal.set(year, 10-1, 12, 13, 25, 00);
return cal.getTimeInMillis();
}
static long getDateMillis(int year) {
return switch (year) {
case 1960 -> Date.parse("12 Oct 1960 13:25:00");
case 1980 -> Date.parse("12 Oct 1980 13:25:00");
default -> (new Date()).getTime();
};
}
static void touch(long millis) throws IOException {
final File parent = new File("log/FileTimes"), file;
parent.mkdirs();
try (FileOutputStream out = new FileOutputStream(file = new File(parent, "FileTime"))) {
out.close();
Files.setLastModifiedTime(file.toPath(), FileTime.fromMillis(millis));
System.out.println("Date of file: "+new Date(file.lastModified()));
}
}
public static void main(String[] args) throws IOException, InterruptedException {
for (int i=0; i<3; i++) {
long millis = getDateMillis(1960);
System.out.println("Millies from Date: "+millis);
touch(millis);
millis = getCalendarMillis(1960);
System.out.println("Millies from Calendar: "+millis);
touch(millis);
millis = millis / 1000 * 1000 - 1000;
System.out.println("Millies from Cal rounded down: "+millis);
touch(millis);
Thread.sleep(250);
System.out.println();
}
}
}
I get:
Millies from Date: -290950500000
Date of file: Wed Oct 12 13:25:00 CET 1960
Millies from Calendar: -290950499926
Date of file: Thu Jan 01 01:00:00 CET 1970
Millies from Cal rounded down: -290950500000
Date of file: Wed Oct 12 13:25:00 CET 1960
Millies from Date: -290950500000
Date of file: Wed Oct 12 13:25:00 CET 1960
Millies from Calendar: -290950499532
Date of file: Thu Jan 01 01:00:00 CET 1970
Millies from Cal rounded down: -290950500000
Date of file: Wed Oct 12 13:25:00 CET 1960
Millies from Date: -290950500000
Date of file: Wed Oct 12 13:25:00 CET 1960
Millies from Calendar: -290950499230
Date of file: Thu Jan 01 01:00:00 CET 1970
Millies from Cal rounded down: -290950500000
Date of file: Wed Oct 12 13:25:00 CET 1960
In the end I get unknown date with the Nemo file manager on Linux when using:
Calendar cal ... cal.set(...);.
Note, that the millis are little different on each invocation!
But I get the correct date when using deprecated:
Date.parse(...); or I round down the milliseconds.
To me this looks like a bug in Java. What you think?
Here I'm at CET.
Irrespective of how timestamps before the UNIX epoch are represented, how does it make sense for a file to be last modified >before< 1970? UNIX / Linux didn't exist then, so neither could a file. The timestamps are for the file, not for the information that the file represents; e.g. its contents.
What you are trying to represent here would be better represented some other way; i.e. not by hijacking file system timestamps for a purpose they weren't intended for.
When external tools on some systems (ls, the Linux file manager) behave strangely with pre-epoch file timestamps, it is probably because the designers of said tools are treating those timestamps as bad data. There's probably nothing you can do about that.
As noted, Java can handle timestamps before the epoch. If you are using "seconds since the epoch" representation, just use a negative integer.
You tried using File.setLastModified. That fails because the method has a hardwired check for negative timestamps. (Per the OpenJDK Java 11 source code.)
In your updated question, you claim that this documented behavior is a bug in Java. Well, maybe if you had reported it in 1997 against Java 1.0 it would have been possible to fix it. However, java.io.File is a legacy class that is full of inconsistent and/or undocumented behavior that can't be fixed without breaking "millions" of existing Java applications written in the last nearly 30 years. If you want consistent, well documented behavior, use Path and Files. These and other NIO classes were added in Java 7 in 2011 to (among other things) help Java to "play nicely" with various OS dependent file system features.
You should be using Files.setLastModifiedTime instead. Its javadoc says this.
public static Path setLastModifiedTime(Path path, FileTime time) throws IOExceptionUpdates a file's last modified time attribute. The file time is converted to the epoch and precision supported by the file system. Converting from finer to coarser granularities result in precision loss. The behavior of this method when attempting to set the last modified time when it is not supported by the file system or is outside the range supported by the underlying file store is not defined. It may or not fail by throwing an
IOException.
The implication is that if the file system support timestamps prior to the UNIX epoch, then setLastModifiedTime should work.
Looking (again) at the OpenJDK Java 11 source code for the UNIX / Linux case, it will attempt to set the modified time that you gave. If the syscall to set the timestamp(s) fails with an EINVAL, and the timestamp was negative, it will try again setting the timestamp to zero instead. (Note that this behavior is not documented, so it could conceivably change between Java versions.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With