FileTimes.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.io.file.attribute;

  18. import java.io.IOException;
  19. import java.math.BigDecimal;
  20. import java.math.MathContext;
  21. import java.math.RoundingMode;
  22. import java.nio.file.Files;
  23. import java.nio.file.Path;
  24. import java.nio.file.attribute.FileTime;
  25. import java.time.Instant;
  26. import java.util.Date;
  27. import java.util.concurrent.TimeUnit;

  28. /**
  29.  * Helps use {@link FileTime} and interoperate Date and NTFS times.
  30.  * <p>
  31.  * An NTFS 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
  32.  * Universal Time (UTC). This is the offset of Windows time 0 to Unix epoch in 100-nanosecond intervals.
  33.  * </p>
  34.  *
  35.  * @since 2.12.0
  36.  * @see <a href="https://learn.microsoft.com/en-us/windows/win32/sysinfo/file-times">NTFS File Times</a>
  37.  */
  38. public final class FileTimes {

  39.     private static final BigDecimal LONG_MIN_VALUE_BD = BigDecimal.valueOf(Long.MIN_VALUE);

  40.     private static final BigDecimal LONG_MAX_VALUE_BD = BigDecimal.valueOf(Long.MAX_VALUE);

  41.     private static final MathContext MATH_CONTEXT = new MathContext(0, RoundingMode.FLOOR);

  42.     /**
  43.      * Constant for the {@code 1970-01-01T00:00:00Z} {@link Instant#EPOCH epoch} as a time stamp attribute.
  44.      *
  45.      * @see Instant#EPOCH
  46.      */
  47.     public static final FileTime EPOCH = FileTime.from(Instant.EPOCH);

  48.     /**
  49.      * The offset of Windows time 0 to Unix epoch in 100-nanosecond intervals.
  50.      *
  51.      * <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms724290%28v=vs.85%29.aspx">Windows File Times</a>
  52.      * <p>
  53.      * 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
  54.      * Universal Time (UTC). This is the offset of Windows time 0 to Unix epoch in 100-nanosecond intervals.
  55.      * </p>
  56.      */
  57.     static final long UNIX_TO_NTFS_OFFSET = -116444736000000000L;

  58.     private static final BigDecimal UNIX_TO_NTFS_OFFSET_BD = BigDecimal.valueOf(UNIX_TO_NTFS_OFFSET);

  59.     /**
  60.      * The amount of 100-nanosecond intervals in one second.
  61.      */
  62.     private static final long HUNDRED_NANOS_PER_SECOND = TimeUnit.SECONDS.toNanos(1) / 100;

  63.     private static final BigDecimal HUNDRED_NANOS_PER_SECOND_BD = BigDecimal.valueOf(HUNDRED_NANOS_PER_SECOND);

  64.     /**
  65.      * The amount of 100-nanosecond intervals in one millisecond.
  66.      */
  67.     static final long HUNDRED_NANOS_PER_MILLISECOND = TimeUnit.MILLISECONDS.toNanos(1) / 100;

  68.     static final BigDecimal HUNDRED_NANOS_PER_MILLISECOND_BD = BigDecimal.valueOf(HUNDRED_NANOS_PER_MILLISECOND);

  69.     private static final long HUNDRED = 100L;

  70.     private static final BigDecimal HUNDRED_BD = BigDecimal.valueOf(HUNDRED);

  71.     /**
  72.      * Converts standard Unix time (in seconds, UTC/GMT) to {@link FileTime}.
  73.      *
  74.      * @param time Unix timestamp (seconds).
  75.      * @return the corresponding FileTime.
  76.      * @since 2.16.0
  77.      */
  78.     public static FileTime fromUnixTime(final long time) {
  79.         return FileTime.from(time, TimeUnit.SECONDS);
  80.     }

  81.     /**
  82.      * Tests whether a FileTime can be safely represented in the standard Unix time.
  83.      * <p>
  84.      * If the FileTime is null, this method returns true.
  85.      * </p>
  86.      *
  87.      * @param time the FileTime to evaluate, can be null.
  88.      * @return true if the time exceeds the minimum or maximum Unix time, false otherwise.
  89.      * @since 2.16.0
  90.      */
  91.     public static boolean isUnixTime(final FileTime time) {
  92.         return isUnixTime(toUnixTime(time));
  93.     }

  94.     /**
  95.      * Tests whether a given number of seconds (since Epoch) can be safely represented in the standard Unix time.
  96.      *
  97.      * @param seconds the number of seconds (since Epoch) to evaluate.
  98.      * @return true if the time can be represented in the standard Unix time, false otherwise.
  99.      * @since 2.16.0
  100.      */
  101.     public static boolean isUnixTime(final long seconds) {
  102.         return Integer.MIN_VALUE <= seconds && seconds <= Integer.MAX_VALUE;
  103.     }

  104.     /**
  105.      * Subtracts milliseconds from a source FileTime.
  106.      *
  107.      * @param fileTime         The source FileTime.
  108.      * @param millisToSubtract The milliseconds to subtract.
  109.      * @return The resulting FileTime.
  110.      */
  111.     public static FileTime minusMillis(final FileTime fileTime, final long millisToSubtract) {
  112.         return FileTime.from(fileTime.toInstant().minusMillis(millisToSubtract));
  113.     }

  114.     /**
  115.      * Subtracts nanoseconds from a source FileTime.
  116.      *
  117.      * @param fileTime        The source FileTime.
  118.      * @param nanosToSubtract The nanoseconds to subtract.
  119.      * @return The resulting FileTime.
  120.      */
  121.     public static FileTime minusNanos(final FileTime fileTime, final long nanosToSubtract) {
  122.         return FileTime.from(fileTime.toInstant().minusNanos(nanosToSubtract));
  123.     }

  124.     /**
  125.      * Subtracts seconds from a source FileTime.
  126.      *
  127.      * @param fileTime          The source FileTime.
  128.      * @param secondsToSubtract The seconds to subtract.
  129.      * @return The resulting FileTime.
  130.      */
  131.     public static FileTime minusSeconds(final FileTime fileTime, final long secondsToSubtract) {
  132.         return FileTime.from(fileTime.toInstant().minusSeconds(secondsToSubtract));
  133.     }

  134.     /**
  135.      * Obtains the current instant FileTime from the system clock.
  136.      *
  137.      * @return the current instant FileTime from the system clock.
  138.      */
  139.     public static FileTime now() {
  140.         return FileTime.from(Instant.now());
  141.     }

  142.     static Date ntfsTimeToDate(final BigDecimal ntfsTime) {
  143.         return new Date(ntfsTimeToInstant(ntfsTime).toEpochMilli());
  144.     }

  145.     /**
  146.      * Converts an NTFS time (100 nanosecond units since 1 January 1601) to a {@link Date}.
  147.      * <p>
  148.      * An NTFS file time is a 64-bit value for the number of 100-nanosecond intervals since 12:00 A.M. January 1, 1601 Coordinated Universal Time (UTC).
  149.      * </p>
  150.      *
  151.      * @param ntfsTime the NTFS time, 100-nanosecond units since 1 January 1601.
  152.      * @return the Date input.
  153.      * @see <a href="https://learn.microsoft.com/en-us/windows/win32/sysinfo/file-times">NTFS File Times</a>
  154.      */
  155.     public static Date ntfsTimeToDate(final long ntfsTime) {
  156.         return ntfsTimeToDate(BigDecimal.valueOf(ntfsTime));
  157.     }

  158.     /**
  159.      * Converts an NTFS time (100-nanosecond units since 1 January 1601) to a {@link FileTime}.
  160.      * <p>
  161.      * An NTFS file time is a 64-bit value for the number of 100-nanosecond intervals since 12:00 A.M. January 1, 1601 Coordinated Universal Time (UTC).
  162.      * </p>
  163.      *
  164.      * @param ntfsTime the NTFS time, 100-nanosecond units since 1 January 1601.
  165.      * @return the FileTime input.
  166.      * @see #toNtfsTime(FileTime)
  167.      * @see <a href="https://learn.microsoft.com/en-us/windows/win32/sysinfo/file-times">NTFS File Times</a>
  168.      */
  169.     public static FileTime ntfsTimeToFileTime(final long ntfsTime) {
  170.         return FileTime.from(ntfsTimeToInstant(ntfsTime));
  171.     }

  172.     static Instant ntfsTimeToInstant(final BigDecimal ntfsTime) {
  173.         final BigDecimal javaHundredsNanos = ntfsTime.add(UNIX_TO_NTFS_OFFSET_BD);
  174.         final BigDecimal[] dar = javaHundredsNanos.divideAndRemainder(HUNDRED_NANOS_PER_SECOND_BD, MATH_CONTEXT);
  175.         return Instant.ofEpochSecond(dar[0].longValueExact(), dar[1].multiply(HUNDRED_BD).longValueExact());
  176.     }

  177.     static Instant ntfsTimeToInstant(final long ntfsTime) {
  178.         return ntfsTimeToInstant(BigDecimal.valueOf(ntfsTime));
  179.     }

  180.     /**
  181.      * Adds milliseconds to a {@link FileTime}.
  182.      *
  183.      * @param fileTime    The source FileTime.
  184.      * @param millisToAdd The milliseconds to add.
  185.      * @return The resulting FileTime.
  186.      */
  187.     public static FileTime plusMillis(final FileTime fileTime, final long millisToAdd) {
  188.         return FileTime.from(fileTime.toInstant().plusMillis(millisToAdd));
  189.     }

  190.     /**
  191.      * Adds nanoseconds to a {@link FileTime}.
  192.      *
  193.      * @param fileTime        The source FileTime.
  194.      * @param nanosToSubtract The nanoseconds to subtract.
  195.      * @return The resulting FileTime.
  196.      */
  197.     public static FileTime plusNanos(final FileTime fileTime, final long nanosToSubtract) {
  198.         return FileTime.from(fileTime.toInstant().plusNanos(nanosToSubtract));
  199.     }

  200.     /**
  201.      * Adds seconds to a {@link FileTime}.
  202.      *
  203.      * @param fileTime     The source FileTime.
  204.      * @param secondsToAdd The seconds to add.
  205.      * @return The resulting FileTime.
  206.      */
  207.     public static FileTime plusSeconds(final FileTime fileTime, final long secondsToAdd) {
  208.         return FileTime.from(fileTime.toInstant().plusSeconds(secondsToAdd));
  209.     }

  210.     /**
  211.      * Sets the last modified time of the given file path to now.
  212.      *
  213.      * @param path The file path to set.
  214.      * @throws IOException if an I/O error occurs.
  215.      */
  216.     public static void setLastModifiedTime(final Path path) throws IOException {
  217.         Files.setLastModifiedTime(path, now());
  218.     }

  219.     /**
  220.      * Converts a {@link FileTime} to a {@link Date}. If the provided FileTime is {@code null}, the returned Date is also {@code null}.
  221.      *
  222.      * @param fileTime the file time to be converted.
  223.      * @return a {@link Date} which corresponds to the supplied time, or {@code null} if the time is {@code null}.
  224.      * @see #toFileTime(Date)
  225.      */
  226.     public static Date toDate(final FileTime fileTime) {
  227.         return fileTime != null ? new Date(fileTime.toMillis()) : null;
  228.     }

  229.     /**
  230.      * Converts a {@link Date} to a {@link FileTime}. If the provided Date is {@code null}, the returned FileTime is also {@code null}.
  231.      *
  232.      * @param date the date to be converted.
  233.      * @return a {@link FileTime} which corresponds to the supplied date, or {@code null} if the date is {@code null}.
  234.      * @see #toDate(FileTime)
  235.      */
  236.     public static FileTime toFileTime(final Date date) {
  237.         return date != null ? FileTime.fromMillis(date.getTime()) : null;
  238.     }

  239.     /**
  240.      * Converts a {@link Date} to NTFS time.
  241.      * <p>
  242.      * An NTFS file time is a 64-bit value for the number of 100-nanosecond intervals since 12:00 A.M. January 1, 1601 Coordinated Universal Time (UTC).
  243.      * </p>
  244.      *
  245.      * @param date the Date input.
  246.      * @return the NTFS time, 100-nanosecond units since 1 January 1601.
  247.      */
  248.     public static long toNtfsTime(final Date date) {
  249.         return toNtfsTime(date.getTime());
  250.     }

  251.     /**
  252.      * Converts a {@link FileTime} to NTFS time (100-nanosecond units since 1 January 1601).
  253.      * <p>
  254.      * An NTFS file time is a 64-bit value for the number of 100-nanosecond intervals since 12:00 A.M. January 1, 1601 Coordinated Universal Time (UTC).
  255.      * </p>
  256.      *
  257.      * @param fileTime the FileTime input.
  258.      * @return the NTFS time, 100-nanosecond units since 1 January 1601.
  259.      */
  260.     public static long toNtfsTime(final FileTime fileTime) {
  261.         return toNtfsTime(fileTime.toInstant());
  262.     }

  263.     static long toNtfsTime(final Instant instant) {
  264.         final BigDecimal javaHundredNanos = BigDecimal.valueOf(instant.getEpochSecond()).multiply(HUNDRED_NANOS_PER_SECOND_BD)
  265.                 .add(BigDecimal.valueOf(instant.getNano() / 100));
  266.         return javaHundredNanos.subtract(UNIX_TO_NTFS_OFFSET_BD).longValueExact();
  267.     }

  268.     /**
  269.      * Converts a Java time (milliseconds since Epoch) to NTFS time.
  270.      * <p>
  271.      * An NTFS file time is a 64-bit value for the number of 100-nanosecond intervals since 12:00 A.M. January 1, 1601 Coordinated Universal Time (UTC).
  272.      * </p>
  273.      *
  274.      * @param javaTime the Java time
  275.      * @return the NTFS time, 100-nanosecond units since 1 January 1601.
  276.      * @since 2.16.0
  277.      */
  278.     public static long toNtfsTime(final long javaTime) {
  279.         final BigDecimal javaHundredNanos = BigDecimal.valueOf(javaTime).multiply(HUNDRED_NANOS_PER_MILLISECOND_BD);
  280.         final BigDecimal ntfsTime = javaHundredNanos.subtract(UNIX_TO_NTFS_OFFSET_BD);
  281.         if (ntfsTime.compareTo(LONG_MAX_VALUE_BD) >= 0) {
  282.             return Long.MAX_VALUE;
  283.         }
  284.         if (ntfsTime.compareTo(LONG_MIN_VALUE_BD) <= 0) {
  285.             return Long.MIN_VALUE;
  286.         }
  287.         return ntfsTime.longValue();
  288.     }

  289.     /**
  290.      * Converts a {@link FileTime} to standard Unix time in seconds.
  291.      * <p>
  292.      * The returned seconds value may lie out of bounds of Unix time. Check with {@link FileTimes#isUnixTime(long)}.
  293.      * </p>
  294.      *
  295.      * @param fileTime the original FileTime.
  296.      * @return the Unix timestamp or 0 if the input is null.
  297.      * @see #isUnixTime(long)
  298.      * @since 2.16.0
  299.      */
  300.     public static long toUnixTime(final FileTime fileTime) {
  301.         return fileTime != null ? fileTime.to(TimeUnit.SECONDS) : 0;
  302.     }

  303.     private FileTimes() {
  304.         // No instances.
  305.     }
  306. }