    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.io.file.attribute;
    019
    020import java.io.IOException;
    021import java.nio.file.Files;
    022import java.nio.file.Path;
    023import java.nio.file.attribute.FileTime;
    024import java.time.Instant;
    025import java.util.Date;
    026import java.util.concurrent.TimeUnit;
    027
    028/**
    029 * Helps use {@link FileTime} and interoperate Date and NTFS times.
    030 *
    031 * @since 2.12.0
    032 */
    033public final class FileTimes {
    034
    035    /**
    036     * Constant for the {@code 1970-01-01T00:00:00Z} {@link Instant#EPOCH epoch} as a time stamp attribute.
    037     *
    038     * @see Instant#EPOCH
    039     */
    040    public static final FileTime EPOCH = FileTime.from(Instant.EPOCH);
    041
    042    /**
    043     * The offset of Windows time 0 to Unix epoch in 100-nanosecond intervals.
    044     *
    045     * <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms724290%28v=vs.85%29.aspx">Windows File Times</a>
    046     * <p>
    047     * A file time is a 64-bit value that represents the number of 100-nanosecond intervals that have elapsed since 12:00
    048     * A.M. January 1, 1601 Coordinated Universal Time (UTC). This is the offset of Windows time 0 to Unix epoch in
    049     * 100-nanosecond intervals.
    050     * </p>
    051     */
    052    static final long WINDOWS_EPOCH_OFFSET = -116444736000000000L;
    053
    054    /**
    055     * The amount of 100-nanosecond intervals in one second.
    056     */
    057    private static final long HUNDRED_NANOS_PER_SECOND = TimeUnit.SECONDS.toNanos(1) / 100;
    058
    059    /**
    060     * The amount of 100-nanosecond intervals in one millisecond.
    061     */
    062    static final long HUNDRED_NANOS_PER_MILLISECOND = TimeUnit.MILLISECONDS.toNanos(1) / 100;
    063
    064    /**
    065     * Subtracts milliseconds from a source FileTime.
    066     *
    067     * @param fileTime The source FileTime.
    068     * @param millisToSubtract The milliseconds to subtract.
    069     * @return The resulting FileTime.
    070     */
    071    public static FileTime minusMillis(final FileTime fileTime, final long millisToSubtract) {
    072        return FileTime.from(fileTime.toInstant().minusMillis(millisToSubtract));
    073    }
    074
    075    /**
    076     * Subtracts nanoseconds from a source FileTime.
    077     *
    078     * @param fileTime The source FileTime.
    079     * @param nanosToSubtract The nanoseconds to subtract.
    080     * @return The resulting FileTime.
    081     */
    082    public static FileTime minusNanos(final FileTime fileTime, final long nanosToSubtract) {
    083        return FileTime.from(fileTime.toInstant().minusNanos(nanosToSubtract));
    084    }
    085
    086    /**
    087     * Subtracts seconds from a source FileTime.
    088     *
    089     * @param fileTime The source FileTime.
    090     * @param secondsToSubtract The seconds to subtract.
    091     * @return The resulting FileTime.
    092     */
    093    public static FileTime minusSeconds(final FileTime fileTime, final long secondsToSubtract) {
    094        return FileTime.from(fileTime.toInstant().minusSeconds(secondsToSubtract));
    095    }
    096
    097    /**
    098     * Obtains the current instant FileTime from the system clock.
    099     *
    100     * @return the current instant FileTime from the system clock.
    101     */
    102    public static FileTime now() {
    103        return FileTime.from(Instant.now());
    104    }
    105
    106    /**
    107     * Converts NTFS time (100 nanosecond units since 1 January 1601) to Java time.
    108     *
    109     * @param ntfsTime the NTFS time in 100 nanosecond units
    110     * @return the Date
    111     */
    112    public static Date ntfsTimeToDate(final long ntfsTime) {
    113        final long javaHundredNanos = Math.addExact(ntfsTime, WINDOWS_EPOCH_OFFSET);
    114        final long javaMillis = Math.floorDiv(javaHundredNanos, HUNDRED_NANOS_PER_MILLISECOND);
    115        return new Date(javaMillis);
    116    }
    117
    118    /**
    119     * Converts NTFS time (100-nanosecond units since 1 January 1601) to a FileTime.
    120     *
    121     * @param ntfsTime the NTFS time in 100-nanosecond units
    122     * @return the FileTime
    123     *
    124     * @see #toNtfsTime(FileTime)
    125     */
    126    public static FileTime ntfsTimeToFileTime(final long ntfsTime) {
    127        final long javaHundredsNanos = Math.addExact(ntfsTime, WINDOWS_EPOCH_OFFSET);
    128        final long javaSeconds = Math.floorDiv(javaHundredsNanos, HUNDRED_NANOS_PER_SECOND);
    129        final long javaNanos = Math.floorMod(javaHundredsNanos, HUNDRED_NANOS_PER_SECOND) * 100;
    130        return FileTime.from(Instant.ofEpochSecond(javaSeconds, javaNanos));
    131    }
    132
    133    /**
    134     * Adds milliseconds to a source FileTime.
    135     *
    136     * @param fileTime The source FileTime.
    137     * @param millisToAdd The milliseconds to add.
    138     * @return The resulting FileTime.
    139     */
    140    public static FileTime plusMillis(final FileTime fileTime, final long millisToAdd) {
    141        return FileTime.from(fileTime.toInstant().plusMillis(millisToAdd));
    142    }
    143
    144    /**
    145     * Adds nanoseconds from a source FileTime.
    146     *
    147     * @param fileTime The source FileTime.
    148     * @param nanosToSubtract The nanoseconds to subtract.
    149     * @return The resulting FileTime.
    150     */
    151    public static FileTime plusNanos(final FileTime fileTime, final long nanosToSubtract) {
    152        return FileTime.from(fileTime.toInstant().plusNanos(nanosToSubtract));
    153    }
    154
    155    /**
    156     * Adds seconds to a source FileTime.
    157     *
    158     * @param fileTime The source FileTime.
    159     * @param secondsToAdd The seconds to add.
    160     * @return The resulting FileTime.
    161     */
    162    public static FileTime plusSeconds(final FileTime fileTime, final long secondsToAdd) {
    163        return FileTime.from(fileTime.toInstant().plusSeconds(secondsToAdd));
    164    }
    165
    166    /**
    167     * Sets the last modified time of the given file path to now.
    168     *
    169     * @param path The file path to set.
    170     * @throws IOException if an I/O error occurs.
    171     */
    172    public static void setLastModifiedTime(final Path path) throws IOException {
    173        Files.setLastModifiedTime(path, now());
    174    }
    175
    176    /**
    177     * Converts {@link FileTime} to a {@link Date}. If the provided FileTime is {@code null}, the returned Date is also
    178     * {@code null}.
    179     *
    180     * @param fileTime the file time to be converted.
    181     * @return a {@link Date} which corresponds to the supplied time, or {@code null} if the time is {@code null}.
    182     * @see #toFileTime(Date)
    183     */
    184    public static Date toDate(final FileTime fileTime) {
    185        return fileTime != null ? new Date(fileTime.toMillis()) : null;
    186    }
    187
    188    /**
    189     * Converts {@link Date} to a {@link FileTime}. If the provided Date is {@code null}, the returned FileTime is also
    190     * {@code null}.
    191     *
    192     * @param date the date to be converted.
    193     * @return a {@link FileTime} which corresponds to the supplied date, or {@code null} if the date is {@code null}.
    194     * @see #toDate(FileTime)
    195     */
    196    public static FileTime toFileTime(final Date date) {
    197        return date != null ? FileTime.fromMillis(date.getTime()) : null;
    198    }
    199
    200    /**
    201     * Converts a {@link Date} to NTFS time.
    202     *
    203     * @param date the Date
    204     * @return the NTFS time
    205     */
    206    public static long toNtfsTime(final Date date) {
    207        final long javaHundredNanos = date.getTime() * HUNDRED_NANOS_PER_MILLISECOND;
    208        return Math.subtractExact(javaHundredNanos, WINDOWS_EPOCH_OFFSET);
    209    }
    210
    211    /**
    212     * Converts a {@link FileTime} to NTFS time (100-nanosecond units since 1 January 1601).
    213     *
    214     * @param fileTime the FileTime
    215     * @return the NTFS time in 100-nanosecond units
    216     */
    217    public static long toNtfsTime(final FileTime fileTime) {
    218        final Instant instant = fileTime.toInstant();
    219        final long javaHundredNanos = instant.getEpochSecond() * HUNDRED_NANOS_PER_SECOND + instant.getNano() / 100;
    220        return Math.subtractExact(javaHundredNanos, WINDOWS_EPOCH_OFFSET);
    221    }
    222
    223    private FileTimes() {
    224        // No instances.
    225    }
    226}