This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-io.git


The following commit(s) were added to refs/heads/master by this push:
     new 0789b2bd Add NTFS conversion methods from 
https://github.com/apache/commons-compress/pull/256
0789b2bd is described below

commit 0789b2bd4bd554c6eac1f822dec0259da06d7d51
Author: Gary Gregory <[email protected]>
AuthorDate: Sun Apr 17 10:16:10 2022 -0400

    Add NTFS conversion methods from
    https://github.com/apache/commons-compress/pull/256
---
 .../commons/io/file/attribute/FileTimes.java       | 102 +++++++++++++++++++-
 .../commons/io/file/attribute/FileTimesTest.java   | 106 ++++++++++++++++++++-
 2 files changed, 205 insertions(+), 3 deletions(-)

diff --git a/src/main/java/org/apache/commons/io/file/attribute/FileTimes.java 
b/src/main/java/org/apache/commons/io/file/attribute/FileTimes.java
index d7e2cec5..ab84211f 100644
--- a/src/main/java/org/apache/commons/io/file/attribute/FileTimes.java
+++ b/src/main/java/org/apache/commons/io/file/attribute/FileTimes.java
@@ -22,9 +22,11 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.attribute.FileTime;
 import java.time.Instant;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
 
 /**
- * Helps use {@link FileTime}.
+ * Helps use {@link FileTime} and interoperate Date and NTFS times.
  *
  * @since 2.12.0
  */
@@ -37,6 +39,28 @@ public class FileTimes {
      */
     public static final FileTime EPOCH = FileTime.from(Instant.EPOCH);
 
+    /**
+     * The offset of Windows time 0 to Unix epoch in 100-nanosecond intervals.
+     *
+     * <a 
href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms724290%28v=vs.85%29.aspx";>Windows
 File Times</a>
+     * <p>
+     * A file time is a 64-bit value that represents the number of 
100-nanosecond intervals that have elapsed since 12:00
+     * A.M. January 1, 1601 Coordinated Universal Time (UTC). This is the 
offset of Windows time 0 to Unix epoch in
+     * 100-nanosecond intervals.
+     * </p>
+     */
+    static final long WINDOWS_EPOCH_OFFSET = -116444736000000000L;
+
+    /**
+     * The amount of 100-nanosecond intervals in one second.
+     */
+    private static final long HUNDRED_NANOS_PER_SECOND = 
TimeUnit.SECONDS.toNanos(1) / 100;
+
+    /**
+     * The amount of 100-nanosecond intervals in one millisecond.
+     */
+    static final long HUNDRED_NANOS_PER_MILLISECOND = 
TimeUnit.MILLISECONDS.toNanos(1) / 100;
+
     /**
      * Subtracts milliseconds from a source FileTime.
      *
@@ -71,7 +95,7 @@ public class FileTimes {
     }
 
     /**
-     * Returns the current instant FileTime from the system clock.
+     * Obtains the current instant FileTime from the system clock.
      *
      * @return the current instant FileTime from the system clock.
      */
@@ -79,6 +103,33 @@ public class FileTimes {
         return FileTime.from(Instant.now());
     }
 
+    /**
+     * Converts NTFS time (100 nanosecond units since 1 January 1601) to Java 
time.
+     *
+     * @param ntfsTime the NTFS time in 100 nanosecond units
+     * @return the Date
+     */
+    public static Date ntfsTimeToDate(final long ntfsTime) {
+        final long javaHundredNanos = Math.addExact(ntfsTime, 
WINDOWS_EPOCH_OFFSET);
+        final long javaMillis = Math.floorDiv(javaHundredNanos, 
HUNDRED_NANOS_PER_MILLISECOND);
+        return new Date(javaMillis);
+    }
+
+    /**
+     * Converts NTFS time (100-nanosecond units since 1 January 1601) to a 
FileTime.
+     *
+     * @param ntfsTime the NTFS time in 100-nanosecond units
+     * @return the FileTime
+     *
+     * @see #toNtfsTime(FileTime)
+     */
+    public static FileTime ntfsTimeToFileTime(final long ntfsTime) {
+        final long javaHundredsNanos = Math.addExact(ntfsTime, 
WINDOWS_EPOCH_OFFSET);
+        final long javaSeconds = Math.floorDiv(javaHundredsNanos, 
HUNDRED_NANOS_PER_SECOND);
+        final long javaNanos = Math.floorMod(javaHundredsNanos, 
HUNDRED_NANOS_PER_SECOND) * 100;
+        return FileTime.from(Instant.ofEpochSecond(javaSeconds, javaNanos));
+    }
+
     /**
      * Adds milliseconds to a source FileTime.
      *
@@ -122,6 +173,53 @@ public class FileTimes {
         Files.setLastModifiedTime(path, now());
     }
 
+    /**
+     * Converts {@link FileTime} to a {@link Date}. If the provided FileTime 
is {@code null}, the returned Date is also
+     * {@code null}.
+     *
+     * @param fileTime the file time to be converted.
+     * @return a {@link Date} which corresponds to the supplied time, or 
{@code null} if the time is {@code null}.
+     * @see #toFileTime(Date)
+     */
+    public static Date toDate(final FileTime fileTime) {
+        return fileTime != null ? new Date(fileTime.toMillis()) : null;
+    }
+
+    /**
+     * Converts {@link Date} to a {@link FileTime}. If the provided Date is 
{@code null}, the returned FileTime is also
+     * {@code null}.
+     *
+     * @param date the date to be converted.
+     * @return a {@link FileTime} which corresponds to the supplied date, or 
{@code null} if the date is {@code null}.
+     * @see #toDate(FileTime)
+     */
+    public static FileTime toFileTime(final Date date) {
+        return date != null ? FileTime.fromMillis(date.getTime()) : null;
+    }
+
+    /**
+     * Converts a {@link Date} to NTFS time.
+     *
+     * @param date the Date
+     * @return the NTFS time
+     */
+    public static long toNtfsTime(final Date date) {
+        final long javaHundredNanos = date.getTime() * 
HUNDRED_NANOS_PER_MILLISECOND;
+        return Math.subtractExact(javaHundredNanos, WINDOWS_EPOCH_OFFSET);
+    }
+
+    /**
+     * Converts a {@link FileTime} to NTFS time (100-nanosecond units since 1 
January 1601).
+     *
+     * @param fileTime the FileTime
+     * @return the NTFS time in 100-nanosecond units
+     */
+    public static long toNtfsTime(final FileTime fileTime) {
+        final Instant instant = fileTime.toInstant();
+        final long javaHundredNanos = (instant.getEpochSecond() * 
HUNDRED_NANOS_PER_SECOND) + (instant.getNano() / 100);
+        return Math.subtractExact(javaHundredNanos, WINDOWS_EPOCH_OFFSET);
+    }
+
     private FileTimes() {
         // No instances.
     }
diff --git 
a/src/test/java/org/apache/commons/io/file/attribute/FileTimesTest.java 
b/src/test/java/org/apache/commons/io/file/attribute/FileTimesTest.java
index 9233a346..8d506ccf 100644
--- a/src/test/java/org/apache/commons/io/file/attribute/FileTimesTest.java
+++ b/src/test/java/org/apache/commons/io/file/attribute/FileTimesTest.java
@@ -18,21 +18,103 @@
 package org.apache.commons.io.file.attribute;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
 
+import java.nio.file.attribute.FileTime;
 import java.time.Instant;
+import java.util.Date;
+import java.util.stream.Stream;
 
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
 
 /**
  * Tests {@link FileTimes}.
  */
 public class FileTimesTest {
 
+    public static Stream<Arguments> dateToNtfsProvider() {
+        // @formatter:off
+        return Stream.of(
+            Arguments.of("1601-01-01T00:00:00.000Z", 0),
+            Arguments.of("1601-01-01T00:00:00.000Z", 1),
+            Arguments.of("1600-12-31T23:59:59.999Z", -1),
+            Arguments.of("1601-01-01T00:00:00.001Z", 
FileTimes.HUNDRED_NANOS_PER_MILLISECOND),
+            Arguments.of("1601-01-01T00:00:00.001Z", 
FileTimes.HUNDRED_NANOS_PER_MILLISECOND + 1),
+            Arguments.of("1601-01-01T00:00:00.000Z", 
FileTimes.HUNDRED_NANOS_PER_MILLISECOND - 1),
+            Arguments.of("1600-12-31T23:59:59.999Z", 
-FileTimes.HUNDRED_NANOS_PER_MILLISECOND),
+            Arguments.of("1600-12-31T23:59:59.999Z", 
-FileTimes.HUNDRED_NANOS_PER_MILLISECOND + 1),
+            Arguments.of("1600-12-31T23:59:59.998Z", 
-FileTimes.HUNDRED_NANOS_PER_MILLISECOND - 1),
+            Arguments.of("1970-01-01T00:00:00.000Z", 
-FileTimes.WINDOWS_EPOCH_OFFSET),
+            Arguments.of("1970-01-01T00:00:00.000Z", 
-FileTimes.WINDOWS_EPOCH_OFFSET + 1),
+            Arguments.of("1970-01-01T00:00:00.001Z", 
-FileTimes.WINDOWS_EPOCH_OFFSET + FileTimes.HUNDRED_NANOS_PER_MILLISECOND),
+            Arguments.of("1969-12-31T23:59:59.999Z", 
-FileTimes.WINDOWS_EPOCH_OFFSET - 1),
+            Arguments.of("1969-12-31T23:59:59.999Z", 
-FileTimes.WINDOWS_EPOCH_OFFSET - FileTimes.HUNDRED_NANOS_PER_MILLISECOND));
+        // @formatter:on
+    }
+
+    public static Stream<Arguments> fileTimeToNtfsProvider() {
+        // @formatter:off
+        return Stream.of(
+            Arguments.of("1601-01-01T00:00:00.0000000Z", 0),
+            Arguments.of("1601-01-01T00:00:00.0000001Z", 1),
+            Arguments.of("1600-12-31T23:59:59.9999999Z", -1),
+            Arguments.of("1601-01-01T00:00:00.0010000Z", 
FileTimes.HUNDRED_NANOS_PER_MILLISECOND),
+            Arguments.of("1601-01-01T00:00:00.0010001Z", 
FileTimes.HUNDRED_NANOS_PER_MILLISECOND + 1),
+            Arguments.of("1601-01-01T00:00:00.0009999Z", 
FileTimes.HUNDRED_NANOS_PER_MILLISECOND - 1),
+            Arguments.of("1600-12-31T23:59:59.9990000Z", 
-FileTimes.HUNDRED_NANOS_PER_MILLISECOND),
+            Arguments.of("1600-12-31T23:59:59.9990001Z", 
-FileTimes.HUNDRED_NANOS_PER_MILLISECOND + 1),
+            Arguments.of("1600-12-31T23:59:59.9989999Z", 
-FileTimes.HUNDRED_NANOS_PER_MILLISECOND - 1),
+            Arguments.of("1970-01-01T00:00:00.0000000Z", 
-FileTimes.WINDOWS_EPOCH_OFFSET),
+            Arguments.of("1970-01-01T00:00:00.0000001Z", 
-FileTimes.WINDOWS_EPOCH_OFFSET + 1),
+            Arguments.of("1970-01-01T00:00:00.0010000Z", 
-FileTimes.WINDOWS_EPOCH_OFFSET + FileTimes.HUNDRED_NANOS_PER_MILLISECOND),
+            Arguments.of("1969-12-31T23:59:59.9999999Z", 
-FileTimes.WINDOWS_EPOCH_OFFSET - 1),
+            Arguments.of("1969-12-31T23:59:59.9990000Z", 
-FileTimes.WINDOWS_EPOCH_OFFSET - FileTimes.HUNDRED_NANOS_PER_MILLISECOND));
+        // @formatter:on
+    }
+
+    @ParameterizedTest
+    @MethodSource("dateToNtfsProvider")
+    public void testDateToFileTime(final String instant, final long ignored) {
+        final Instant parsedInstant = Instant.parse(instant);
+        final FileTime parsedFileTime = FileTime.from(parsedInstant);
+        final Date parsedDate = Date.from(parsedInstant);
+        assertEquals(parsedFileTime, FileTimes.toFileTime(parsedDate));
+    }
+
+    @ParameterizedTest
+    @MethodSource("dateToNtfsProvider")
+    public void testDateToNtfsTime(final String instant, final long ntfsTime) {
+        final long ntfsMillis = Math.floorDiv(ntfsTime, 
FileTimes.HUNDRED_NANOS_PER_MILLISECOND) * 
FileTimes.HUNDRED_NANOS_PER_MILLISECOND;
+        final Date parsed = Date.from(Instant.parse(instant));
+        assertEquals(ntfsMillis, FileTimes.toNtfsTime(parsed));
+    }
+
     @Test
     public void testEpoch() {
         assertEquals(0, FileTimes.EPOCH.toMillis());
     }
 
+    @ParameterizedTest
+    @MethodSource("fileTimeToNtfsProvider")
+    public void testFileTimeToDate(final String instant, final long ignored) {
+        final Instant parsedInstant = Instant.parse(instant);
+        final FileTime parsedFileTime = FileTime.from(parsedInstant);
+        final Date parsedDate = Date.from(parsedInstant);
+        assertEquals(parsedDate, FileTimes.toDate(parsedFileTime));
+    }
+
+    @ParameterizedTest
+    @MethodSource("fileTimeToNtfsProvider")
+    public void testFileTimeToNtfsTime(final String instant, final long 
ntfsTime) {
+        final FileTime parsed = FileTime.from(Instant.parse(instant));
+        assertEquals(ntfsTime, FileTimes.toNtfsTime(parsed));
+    }
+
+    //
+
     @Test
     public void testMinusMillis() {
         final int millis = 2;
@@ -54,6 +136,29 @@ public class FileTimesTest {
         assertEquals(Instant.EPOCH, FileTimes.minusSeconds(FileTimes.EPOCH, 
0).toInstant());
     }
 
+    @ParameterizedTest
+    @MethodSource("dateToNtfsProvider")
+    public void testNtfsTimeToDate(final String instant, final long ntfsTime) {
+        assertEquals(Instant.parse(instant), 
FileTimes.ntfsTimeToDate(ntfsTime).toInstant());
+    }
+
+    @ParameterizedTest
+    @MethodSource("fileTimeToNtfsProvider")
+    public void testNtfsTimeToFileTime(final String instant, final long 
ntfsTime) {
+        final FileTime parsed = FileTime.from(Instant.parse(instant));
+        assertEquals(parsed, FileTimes.ntfsTimeToFileTime(ntfsTime));
+    }
+
+    @Test
+    public void testNullDateToNullFileTime() {
+        assertNull(FileTimes.toFileTime(null));
+    }
+
+    @Test
+    public void testNullFileTimeToNullDate() {
+        assertNull(FileTimes.toDate(null));
+    }
+
     @Test
     public void testPlusMinusMillis() {
         final int millis = 2;
@@ -74,5 +179,4 @@ public class FileTimesTest {
         assertEquals(Instant.EPOCH.plusSeconds(seconds), 
FileTimes.plusSeconds(FileTimes.EPOCH, seconds).toInstant());
         assertEquals(Instant.EPOCH, FileTimes.plusSeconds(FileTimes.EPOCH, 
0).toInstant());
     }
-
 }

Reply via email to