Add and use PathUtils.isNewer(Path, ChronoZonedDateTime, LinkOption...)
for more precision.
Add and use PathUtils.isNewer(Path, Path) for more precision.
Add and use FileUtils.isNewer(File, FileTime) for more precision.
Tailer uses more precise file time checks.
MODIFY
src/changes/changes.xml
src/changes/changes.xml
@@ -176,8 +176,13 @@ The <action> type attribute can be add,update,fix,remove.
Add FileUtils.current().
</action>
<action dev="ggregory" type="add" due-to="Gary Gregory">
- Add and use PathUtils.setLastModifiedTime(Path) for better precision.
- Add and use PathUtils.setLastModifiedTime(Path, Path) for better precision.
+ Add and use PathUtils.setLastModifiedTime(Path) for more precision.
+ Add and use PathUtils.setLastModifiedTime(Path, Path) for more precision.
+ </action>
+ <action dev="ggregory" type="add" due-to="Gary Gregory">
+ Add and use PathUtils.isNewer(Path, ChronoZonedDateTime, LinkOption...) for more precision.
+ Add and use PathUtils.isNewer(Path, Path) for more precision.
+ Add and use FileUtils.isNewer(File, FileTime) for more precision.
</action>
<!-- UPDATE -->
<action dev="ggregory" type="add" due-to="Gary Gregory">

MODIFY
src/main/java/org/apache/commons/io/FileUtils.java
src/main/java/org/apache/commons/io/FileUtils.java
@@ -1560,6 +1560,22 @@ public static boolean isFileNewer(final File file, final ChronoLocalDate chronoL
return isFileNewer(file, chronoLocalDate, LocalTime.now());
}
+ /**
+ * Tests if the specified {@code File} is newer than the specified {@code FileTime}.
+ *
+ * @param file the {@code File} of which the modification date must be compared.
+ * @param fileTime the file time reference.
+ * @return true if the {@code File} exists and has been modified after the given {@code FileTime}.
+ * @throws IOException if an I/O error occurs.
+ * @throws NullPointerException if the file or local date is {@code null}.
+ *
+ * @since 2.12.0
+ */
+ public static boolean isFileNewer(final File file, final FileTime fileTime) throws IOException {
+ Objects.requireNonNull(file, "file");
+ return PathUtils.isNewer(file.toPath(), fileTime);
+ }
+
/**
* Tests if the specified {@code File} is newer than the specified {@code ChronoLocalDate}
* at the specified time.
@@ -1637,7 +1653,12 @@ public static boolean isFileNewer(final File file, final ChronoLocalDateTime<?>
*/
public static boolean isFileNewer(final File file, final ChronoZonedDateTime<?> chronoZonedDateTime) {
Objects.requireNonNull(chronoZonedDateTime, "chronoZonedDateTime");
- return isFileNewer(file, chronoZonedDateTime.toInstant());
+ try {
+ return PathUtils.isNewer(file.toPath(), chronoZonedDateTime);
+ } catch (final IOException e) {
+ // TODO Update method signature
+ throw new UncheckedIOException(e);
+ }
}
/**
@@ -1666,7 +1687,12 @@ public static boolean isFileNewer(final File file, final Date date) {
*/
public static boolean isFileNewer(final File file, final File reference) {
requireExists(reference, "reference");
- return isFileNewer(file, lastModifiedUnchecked(reference));
+ try {
+ return PathUtils.isNewer(file.toPath(), reference.toPath());
+ } catch (final IOException e) {
+ // TODO Update method signature
+ throw new UncheckedIOException(e);
+ }
}
/**
@@ -1681,7 +1707,12 @@ public static boolean isFileNewer(final File file, final File reference) {
*/
public static boolean isFileNewer(final File file, final Instant instant) {
Objects.requireNonNull(instant, "instant");
- return isFileNewer(file, instant.toEpochMilli());
+ try {
+ return PathUtils.isNewer(file.toPath(), instant);
+ } catch (final IOException e) {
+ // TODO Update method signature
+ throw new UncheckedIOException(e);
+ }
}
/**
@@ -1695,7 +1726,12 @@ public static boolean isFileNewer(final File file, final Instant instant) {
*/
public static boolean isFileNewer(final File file, final long timeMillis) {
Objects.requireNonNull(file, "file");
- return file.exists() && lastModifiedUnchecked(file) > timeMillis;
+ try {
+ return PathUtils.isNewer(file.toPath(), timeMillis);
+ } catch (final IOException e) {
+ // TODO Update method signature
+ throw new UncheckedIOException(e);
+ }
}
/**

MODIFY
src/main/java/org/apache/commons/io/file/PathUtils.java
src/main/java/org/apache/commons/io/file/PathUtils.java
import java.nio.file.attribute.PosixFilePermission;
import java.time.Duration;
import java.time.Instant;
+import java.time.chrono.ChronoZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -219,6 +220,20 @@ public static PathCounters cleanDirectory(final Path directory, final DeleteOpti
return visitFileTree(new CleaningPathVisitor(Counters.longPathCounters(), deleteOptions), directory).getPathCounters();
}
+ /**
+ * Compares the specified {@code Path}'s last modified time to the given file time.
+ *
+ * @param file the {@code Path} of which the modification date must be compared
+ * @param fileTime the time reference.
+ * @param options options indicating how symbolic links are handled been modified after the given time reference.
+ * @return See {@link FileTime#compareTo(FileTime)}
+ * @throws IOException if an I/O error occurs.
+ * @throws NullPointerException if the file is {@code null}
+ */
+ private static int compareLastModifiedTimeTo(final Path file, final FileTime fileTime, final LinkOption... options) throws IOException {
+ return Files.getLastModifiedTime(file, options).compareTo(fileTime);
+ }
+
/**
* Copies a directory to another directory.
*
@@ -511,7 +526,7 @@ public static boolean directoryAndFileContentEquals(final Path path1, final Path
if (path1 == null || path2 == null) {
return false;
}
- if (Files.notExists(path1) && Files.notExists(path2)) {
+ if (notExists(path1) && notExists(path2)) {
return true;
}
final RelativeSortedPaths relativeSortedPaths = new RelativeSortedPaths(path1, path2, Integer.MAX_VALUE, linkOptions, fileVisitOption);
@@ -765,24 +780,38 @@ public static boolean isEmptyFile(final Path file) throws IOException {
return Files.size(file) <= 0;
}
+ /**
+ * Tests if the specified {@code Path} is newer than the specified time reference.
+ *
+ * @param file the {@code Path} of which the modification date must be compared
+ * @param czdt the time reference.
+ * @param options options indicating how symbolic links are handled been modified after the given time reference.
+ * @return true if the {@code Path} exists and has been modified after the given time reference.
+ * @throws IOException if an I/O error occurs.
+ * @throws NullPointerException if the file is {@code null}
+ * @since 2.12.0
+ */
+ public static boolean isNewer(final Path file, final ChronoZonedDateTime<?> czdt, final LinkOption... options) throws IOException {
+ Objects.requireNonNull(czdt, "czdt");
+ return isNewer(file, czdt.toInstant(), options);
+ }
+
/**
* Tests if the specified {@code Path} is newer than the specified time reference.
*
* @param file the {@code Path} of which the modification date must be compared
* @param fileTime the time reference.
- * @param options options indicating how symbolic links are handled * @return true if the {@code Path} exists and has
- * been modified after the given time reference.
+ * @param options options indicating how symbolic links are handled been modified after the given time reference.
* @return true if the {@code Path} exists and has been modified after the given time reference.
* @throws IOException if an I/O error occurs.
* @throws NullPointerException if the file is {@code null}
* @since 2.12.0
*/
public static boolean isNewer(final Path file, final FileTime fileTime, final LinkOption... options) throws IOException {
- Objects.requireNonNull(file, "file");
- if (Files.notExists(file)) {
+ if (notExists(file)) {
return false;
}
- return Files.getLastModifiedTime(file, options).compareTo(fileTime) > 0;
+ return compareLastModifiedTimeTo(file, fileTime, options) > 0;
}
/**
@@ -798,11 +827,7 @@ public static boolean isNewer(final Path file, final FileTime fileTime, final Li
* @since 2.12.0
*/
public static boolean isNewer(final Path file, final Instant instant, final LinkOption... options) throws IOException {
- Objects.requireNonNull(file, "file");
- if (Files.notExists(file)) {
- return false;
- }
- return Files.getLastModifiedTime(file, options).toInstant().isAfter(instant);
+ return isNewer(file, FileTime.from(instant), options);
}
/**
@@ -821,6 +846,20 @@ public static boolean isNewer(final Path file, final long timeMillis, final Link
return isNewer(file, FileTime.fromMillis(timeMillis), options);
}
+ /**
+ * Tests if the specified {@code Path} is newer than the reference {@code Path}.
+ *
+ * @param file the {@code File} of which the modification date must be compared.
+ * @param reference the {@code File} of which the modification date is used.
+ * @return true if the {@code File} exists and has been modified more
+ * recently than the reference {@code File}.
+ * @throws IOException if an I/O error occurs.
+ * @since 2.12.0
+ */
+ public static boolean isNewer(final Path file, final Path reference) throws IOException {
+ return isNewer(file, Files.getLastModifiedTime(reference));
+ }
+
/**
* Tests whether the specified {@code Path} is a regular file or not. Implemented as a null-safe delegate to
* {@code Files.isRegularFile(Path path, LinkOption... options)}.
@@ -837,6 +876,7 @@ public static boolean isRegularFile(final Path path, final LinkOption... options
return path != null && Files.isRegularFile(path, options);
}
+
/**
* Creates a new DirectoryStream for Paths rooted at the given directory.
*
@@ -869,6 +909,10 @@ public static OutputStream newOutputStream(final Path path, final boolean append
// @formatter:on
}
+ private static boolean notExists(final Path file) {
+ return Files.notExists(Objects.requireNonNull(file, "file"));
+ }
+
/**
* Returns true if the given options contain {@link StandardDeleteOption#OVERRIDE_READ_ONLY}.
*
@@ -942,6 +986,17 @@ public static String readString(final Path path, final Charset charset) throws I
return stream.collect(Collectors.toList());
}
+ /**
+ * Sets the last modified time of the given file path to now.
+ *
+ * @param path The file path to set.
+ * @throws IOException if an I/O error occurs.
+ * @since 2.12.0
+ */
+ public static void setLastModifiedTime(final Path path) throws IOException {
+ Files.setLastModifiedTime(path, FileTime.from(Instant.now()));
+ }
+
/**
* Sets the given {@code targetFile}'s last modified time to the value from {@code sourceFile}.
*
@@ -957,17 +1012,6 @@ public static void setLastModifiedTime(final Path sourceFile, final Path targetF
Files.setLastModifiedTime(targetFile, Files.getLastModifiedTime(sourceFile));
}
- /**
- * Sets the last modified time of the given file path to now.
- *
- * @param path The file path to set.
- * @throws IOException if an I/O error occurs.
- * @since 2.12.0
- */
- public static void setLastModifiedTime(final Path path) throws IOException {
- Files.setLastModifiedTime(path, FileTime.from(Instant.now()));
- }
-
/**
* Sets the given Path to the {@code readOnly} value.
* <p>
@@ -1109,7 +1153,7 @@ public static Path setReadOnly(final Path path, final boolean readOnly, final Li
* @throws NullPointerException if the file is {@code null}.
* @since 2.12.0
*/
- public static boolean waitFor(final Path file, final Duration timeout, LinkOption... options) {
+ public static boolean waitFor(final Path file, final Duration timeout, final LinkOption... options) {
Objects.requireNonNull(file, "file");
final Instant finishInstant = Instant.now().plus(timeout);
boolean interrupted = false;

MODIFY
src/main/java/org/apache/commons/io/file/attribute/FileTimes.java
src/main/java/org/apache/commons/io/file/attribute/FileTimes.java
/**
* Helps use {@link FileTime}.
+ *
+ * @since 2.12.0
*/
public class FileTimes {
/**
- * Constant for the 1970-01-01T00:00:00Z epoch time stamp attribute.
+ * Constant for the {@code 1970-01-01T00:00:00Z} epoch time stamp attribute.
*
* @see Instant#EPOCH
*/
public static final FileTime EPOCH = FileTime.from(Instant.EPOCH);
+ /**
+ * Subtracts milliseconds from a source FileTime.
+ *
+ * @param fileTime The source FileTime.
+ * @param millisToSubtract The milliseconds to subtract.
+ * @return The resulting FileTime.
+ */
+ public static FileTime minusMillis(final FileTime fileTime, final long millisToSubtract) {
+ return FileTime.from(fileTime.toInstant().minusMillis(millisToSubtract));
+ }
+
+ /**
+ * Subtracts nanoseconds from a source FileTime.
+ *
+ * @param fileTime The source FileTime.
+ * @param nanosToSubtract The nanoseconds to subtract.
+ * @return The resulting FileTime.
+ */
+ public static FileTime minusNanos(final FileTime fileTime, final long nanosToSubtract) {
+ return FileTime.from(fileTime.toInstant().minusNanos(nanosToSubtract));
+ }
+
+ /**
+ * Subtracts seconds from a source FileTime.
+ *
+ * @param fileTime The source FileTime.
+ * @param secondsToSubtract The seconds to subtract.
+ * @return The resulting FileTime.
+ */
+ public static FileTime minusSeconds(final FileTime fileTime, final long secondsToSubtract) {
+ return FileTime.from(fileTime.toInstant().minusSeconds(secondsToSubtract));
+ }
+
+ /**
+ * Adds milliseconds to a source FileTime.
+ *
+ * @param fileTime The source FileTime.
+ * @param millisToAdd The milliseconds to add.
+ * @return The resulting FileTime.
+ */
+ public static FileTime plusMillis(final FileTime fileTime, final long millisToAdd) {
+ return FileTime.from(fileTime.toInstant().plusMillis(millisToAdd));
+ }
+
+ /**
+ * Adds nanoseconds from a source FileTime.
+ *
+ * @param fileTime The source FileTime.
+ * @param nanosToSubtract The nanoseconds to subtract.
+ * @return The resulting FileTime.
+ */
+ public static FileTime plusNanos(final FileTime fileTime, final long nanosToSubtract) {
+ return FileTime.from(fileTime.toInstant().plusNanos(nanosToSubtract));
+ }
+
+ /**
+ * Adds seconds to a source FileTime.
+ *
+ * @param fileTime The source FileTime.
+ * @param secondsToAdd The seconds to add.
+ * @return The resulting FileTime.
+ */
+ public static FileTime plusSeconds(final FileTime fileTime, final long secondsToAdd) {
+ return FileTime.from(fileTime.toInstant().plusSeconds(secondsToAdd));
+ }
+
private FileTimes() {
// No instances.
}
-
}

MODIFY
src/main/java/org/apache/commons/io/input/Tailer.java
src/main/java/org/apache/commons/io/input/Tailer.java
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.Charset;
+import java.nio.file.attribute.FileTime;
import java.time.Duration;
import org.apache.commons.io.FileUtils;
@@ -437,7 +438,7 @@ public Duration getDelayDuration() {
public void run() {
RandomAccessFile reader = null;
try {
- long last = 0; // The last time the file was checked for changes
+ FileTime last = FileTime.fromMillis(0); // The last time the file was checked for changes
long position = 0; // position within the file
// Open the file
while (getRun() && reader == null) {
@@ -451,7 +452,7 @@ public void run() {
} else {
// The current position in the file
position = end ? file.length() : 0;
- last = FileUtils.lastModified(file);
+ last = FileUtils.lastModifiedFileTime(file);
reader.seek(position);
}
}
@@ -486,7 +487,7 @@ public void run() {
if (length > position) {
// The file has more content than it did last time
position = readLines(reader);
- last = FileUtils.lastModified(file);
+ last = FileUtils.lastModifiedFileTime(file);
} else if (newer) {
/*
* This can happen if the file is truncated or overwritten with the exact same length of
@@ -497,7 +498,7 @@ public void run() {
// Now we can read new lines
position = readLines(reader);
- last = FileUtils.lastModified(file);
+ last = FileUtils.lastModifiedFileTime(file);
}
if (reOpen && reader != null) {
reader.close();

MODIFY
src/test/java/org/apache/commons/io/FileUtilsFileNewerTestCase.java
src/test/java/org/apache/commons/io/FileUtilsFileNewerTestCase.java
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
+import java.nio.file.attribute.FileTime;
import java.util.Date;
+import java.util.concurrent.TimeUnit;
+import org.apache.commons.io.file.attribute.FileTimes;
import org.apache.commons.io.test.TestUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -50,25 +53,22 @@ public void setUp() throws Exception {
testFile1 = new File(temporaryFolder, "file1-test.txt");
testFile2 = new File(temporaryFolder, "file2-test.txt");
if (!testFile1.getParentFile().exists()) {
- throw new IOException("Cannot create file " + testFile1
- + " as the parent directory does not exist");
+ throw new IOException("Cannot create file " + testFile1 + " as the parent directory does not exist");
}
- try (final BufferedOutputStream output1 =
- new BufferedOutputStream(Files.newOutputStream(testFile1.toPath()))) {
+ try (final BufferedOutputStream output1 = new BufferedOutputStream(Files.newOutputStream(testFile1.toPath()))) {
TestUtils.generateTestData(output1, FILE1_SIZE);
}
if (!testFile2.getParentFile().exists()) {
- throw new IOException("Cannot create file " + testFile2
- + " as the parent directory does not exist");
+ throw new IOException("Cannot create file " + testFile2 + " as the parent directory does not exist");
}
- try (final BufferedOutputStream output =
- new BufferedOutputStream(Files.newOutputStream(testFile2.toPath()))) {
+ try (final BufferedOutputStream output = new BufferedOutputStream(Files.newOutputStream(testFile2.toPath()))) {
TestUtils.generateTestData(output, FILE2_SIZE);
}
}
/**
* Tests the {@code isFileNewer(File, *)} methods which a "normal" file.
+ *
* @throws IOException
*
* @see FileUtils#isFileNewer(File, long)
@@ -81,16 +81,17 @@ public void testIsFileNewer() throws IOException {
throw new IllegalStateException("The testFile1 should exist");
}
- final long fileLastModified = FileUtils.lastModified(testFile1);
- final long TWO_SECOND = 2000;
+ final FileTime fileLastModified = Files.getLastModifiedTime(testFile1.toPath());
+ final long TWO_SECOND = 2;
- testIsFileNewer("two second earlier is not newer" , testFile1, fileLastModified + TWO_SECOND, false);
+ testIsFileNewer("two second earlier is not newer", testFile1, FileTimes.plusSeconds(fileLastModified, TWO_SECOND), false);
testIsFileNewer("same time is not newer", testFile1, fileLastModified, false);
- testIsFileNewer("two second later is newer" , testFile1, fileLastModified - TWO_SECOND, true);
+ testIsFileNewer("two second later is newer", testFile1, FileTimes.minusSeconds(fileLastModified, TWO_SECOND), true);
}
/**
* Tests the {@code isFileNewer(File, *)} methods which a not existing file.
+ *
* @throws IOException if an I/O error occurs.
*
* @see FileUtils#isFileNewer(File, long)
@@ -104,7 +105,7 @@ public void testIsFileNewerImaginaryFile() throws IOException {
throw new IllegalStateException("The imaginary File exists");
}
- testIsFileNewer("imaginary file can be newer" , imaginaryFile, FileUtils.lastModified(testFile2), false);
+ testIsFileNewer("imaginary file can be newer", imaginaryFile, FileUtils.lastModifiedFileTime(testFile2), false);
}
/**
@@ -115,63 +116,57 @@ public void testIsFileNewerImaginaryFile() throws IOException {
* <li>a {@code Date} which represents the time reference</li>
* <li>a temporary file with the same last modification date as the time reference</li>
* </ul>
- * Then compares (with the needed {@code isFileNewer} method) the last modification date of
- * the specified file with the specified time reference, the created {@code Date} and the temporary
- * file.
- * <br>
+ * Then compares (with the needed {@code isFileNewer} method) the last modification date of the specified file with the
+ * specified time reference, the created {@code Date} and the temporary file.
+ * <p>
* The test is successful if the three comparisons return the specified wanted result.
*
* @param description describes the tested situation
* @param file the file of which the last modification date is compared
- * @param time the time reference measured in milliseconds since the epoch
+ * @param fileTime the time reference measured in milliseconds since the epoch
* @param wantedResult the expected result
* @throws IOException if an I/O error occurs.
- *
- * @see FileUtils#isFileNewer(File, long)
- * @see FileUtils#isFileNewer(File, Date)
- * @see FileUtils#isFileNewer(File, File)
*/
- protected void testIsFileNewer(final String description, final File file, final long time, final boolean wantedResult) throws IOException {
- assertEquals(wantedResult, FileUtils.isFileNewer(file, time), description + " - time");
- assertEquals(wantedResult, FileUtils.isFileNewer(file, new Date(time)), description + " - date");
+ protected void testIsFileNewer(final String description, final File file, final FileTime fileTime, final boolean wantedResult) throws IOException {
+ assertEquals(wantedResult, FileUtils.isFileNewer(file, fileTime), () -> description + " - FileTime");
+ assertEquals(wantedResult, FileUtils.isFileNewer(file, fileTime.toInstant()), () -> description + " - Instant");
final File temporaryFile = testFile2;
-
- temporaryFile.setLastModified(time);
- assertEquals(time, FileUtils.lastModified(temporaryFile), "The temporary file hasn't the right last modification date");
- assertEquals(wantedResult, FileUtils.isFileNewer(file, temporaryFile), description + " - file");
+ Files.setLastModifiedTime(temporaryFile.toPath(), fileTime);
+ assertEquals(fileTime, Files.getLastModifiedTime(temporaryFile.toPath()), "The temporary file hasn't the right last modification date");
+ assertEquals(wantedResult, FileUtils.isFileNewer(file, temporaryFile), () -> description + " - file");
}
/**
* Tests the {@code isFileNewer(File, long)} method without specifying a {@code File}.
- * <br>
+ * <p>
* The test is successful if the method throws an {@code IllegalArgumentException}.
+ * </p>
*/
@Test
public void testIsFileNewerNoFile() {
- assertThrows(NullPointerException.class, () -> FileUtils.isFileNewer(null,0),
- "file");
+ assertThrows(NullPointerException.class, () -> FileUtils.isFileNewer(null, 0), "file");
}
/**
* Tests the {@code isFileNewer(File, Date)} method without specifying a {@code Date}.
- * <br>
+ * <p>
* The test is successful if the method throws an {@code IllegalArgumentException}.
+ * </p>
*/
@Test
public void testIsFileNewerNoDate() {
- assertThrows(NullPointerException.class, () -> FileUtils.isFileNewer(testFile1, (Date) null),
- "date");
+ assertThrows(NullPointerException.class, () -> FileUtils.isFileNewer(testFile1, (Date) null), "date");
}
/**
* Tests the {@code isFileNewer(File, File)} method without specifying a reference {@code File}.
- * <br>
+ * <p>
* The test is successful if the method throws an {@code IllegalArgumentException}.
+ * </p>
*/
@Test
public void testIsFileNewerNoFileReference() {
- assertThrows(NullPointerException.class, () -> FileUtils.isFileNewer(testFile1, (File) null),
- "reference");
+ assertThrows(NullPointerException.class, () -> FileUtils.isFileNewer(testFile1, (File) null), "reference");
}
}

MODIFY
src/test/java/org/apache/commons/io/file/attribute/FileTimesTest.java
src/test/java/org/apache/commons/io/file/attribute/FileTimesTest.java
import static org.junit.jupiter.api.Assertions.assertEquals;
+import java.time.Instant;
+
import org.junit.jupiter.api.Test;
/**
*/
public class FileTimesTest {
+ @Test
+ public void PlusMinusMillis() {
+ final int millis = 2;
+ assertEquals(Instant.EPOCH.plusMillis(millis), FileTimes.plusMillis(FileTimes.EPOCH, millis).toInstant());
+ assertEquals(Instant.EPOCH, FileTimes.plusMillis(FileTimes.EPOCH, 0).toInstant());
+ }
+
@Test
public void testEpoch() {
assertEquals(0, FileTimes.EPOCH.toMillis());
}
+ @Test
+ public void testMinusMillis() {
+ final int millis = 2;
+ assertEquals(Instant.EPOCH.minusMillis(millis), FileTimes.minusMillis(FileTimes.EPOCH, millis).toInstant());
+ assertEquals(Instant.EPOCH, FileTimes.minusMillis(FileTimes.EPOCH, 0).toInstant());
+ }
+
+ @Test
+ public void testMinusNanos() {
+ final int millis = 2;
+ assertEquals(Instant.EPOCH.minusNanos(millis), FileTimes.minusNanos(FileTimes.EPOCH, millis).toInstant());
+ assertEquals(Instant.EPOCH, FileTimes.minusNanos(FileTimes.EPOCH, 0).toInstant());
+ }
+
+ @Test
+ public void testMinusSeconds() {
+ final int seconds = 2;
+ assertEquals(Instant.EPOCH.minusSeconds(seconds), FileTimes.minusSeconds(FileTimes.EPOCH, seconds).toInstant());
+ assertEquals(Instant.EPOCH, FileTimes.minusSeconds(FileTimes.EPOCH, 0).toInstant());
+ }
+
+ @Test
+ public void testPlusNanos() {
+ final int millis = 2;
+ assertEquals(Instant.EPOCH.plusNanos(millis), FileTimes.plusNanos(FileTimes.EPOCH, millis).toInstant());
+ assertEquals(Instant.EPOCH, FileTimes.plusNanos(FileTimes.EPOCH, 0).toInstant());
+ }
+
+ @Test
+ public void testPlusSeconds() {
+ final int seconds = 2;
+ assertEquals(Instant.EPOCH.plusSeconds(seconds), FileTimes.plusSeconds(FileTimes.EPOCH, seconds).toInstant());
+ assertEquals(Instant.EPOCH, FileTimes.plusSeconds(FileTimes.EPOCH, 0).toInstant());
+ }
+
}

Add and use PathUtils.isNewer(Path, ChronoZonedDateTime, LinkOption...)
for more precision.

- Add and use PathUtils.isNewer(Path, Path) for more precision.
- Add and use FileUtils.isNewer(File, FileTime) for more precision.
- Tailer uses more precise file time checks.