    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     * Converts standard UNIX time (in seconds, UTC/GMT) to {@link FileTime}.
    066     *
    067     * @param time UNIX timestamp (seconds).
    068     * @return the corresponding FileTime.
    069     * @since 2.16.0
    070     */
    071    public static FileTime fromUnixTime(final long time) {
    072        return FileTime.from(time, TimeUnit.SECONDS);
    073    }
    074
    075    /**
    076     * Tests whether a FileTime can be safely represented in the standard UNIX time.
    077     * <p>
    078     * If the FileTime is null, this method returns true.
    079     * </p>
    080     *
    081     * @param time the FileTime to evaluate, can be null.
    082     * @return true if the time exceeds the minimum or maximum UNIX time, false otherwise.
    083     * @since 2.16.0
    084     */
    085    public static boolean isUnixTime(final FileTime time) {
    086        return isUnixTime(toUnixTime(time));
    087    }
    088
    089    /**
    090     * Tests whether a given number of seconds (since Epoch) can be safely represented in the standard UNIX time.
    091     *
    092     * @param seconds the number of seconds (since Epoch) to evaluate.
    093     * @return true if the time can be represented in the standard UNIX time, false otherwise.
    094     * @since 2.16.0
    095     */
    096    public static boolean isUnixTime(final long seconds) {
    097        return Integer.MIN_VALUE <= seconds && seconds <= Integer.MAX_VALUE;
    098    }
    099
    100
    101    /**
    102     * Subtracts milliseconds from a source FileTime.
    103     *
    104     * @param fileTime The source FileTime.
    105     * @param millisToSubtract The milliseconds to subtract.
    106     * @return The resulting FileTime.
    107     */
    108    public static FileTime minusMillis(final FileTime fileTime, final long millisToSubtract) {
    109        return FileTime.from(fileTime.toInstant().minusMillis(millisToSubtract));
    110    }
    111
    112    /**
    113     * Subtracts nanoseconds from a source FileTime.
    114     *
    115     * @param fileTime The source FileTime.
    116     * @param nanosToSubtract The nanoseconds to subtract.
    117     * @return The resulting FileTime.
    118     */
    119    public static FileTime minusNanos(final FileTime fileTime, final long nanosToSubtract) {
    120        return FileTime.from(fileTime.toInstant().minusNanos(nanosToSubtract));
    121    }
    122
    123    /**
    124     * Subtracts seconds from a source FileTime.
    125     *
    126     * @param fileTime The source FileTime.
    127     * @param secondsToSubtract The seconds to subtract.
    128     * @return The resulting FileTime.
    129     */
    130    public static FileTime minusSeconds(final FileTime fileTime, final long secondsToSubtract) {
    131        return FileTime.from(fileTime.toInstant().minusSeconds(secondsToSubtract));
    132    }
    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
    143    /**
    144     * Converts NTFS time (100 nanosecond units since 1 January 1601) to Java time.
    145     *
    146     * @param ntfsTime the NTFS time in 100 nanosecond units
    147     * @return the Date
    148     */
    149    public static Date ntfsTimeToDate(final long ntfsTime) {
    150        final long javaHundredNanos = Math.addExact(ntfsTime, WINDOWS_EPOCH_OFFSET);
    151        final long javaMillis = Math.floorDiv(javaHundredNanos, HUNDRED_NANOS_PER_MILLISECOND);
    152        return new Date(javaMillis);
    153    }
    154
    155    /**
    156     * Converts NTFS time (100-nanosecond units since 1 January 1601) to a FileTime.
    157     *
    158     * @param ntfsTime the NTFS time in 100-nanosecond units
    159     * @return the FileTime
    160     *
    161     * @see #toNtfsTime(FileTime)
    162     */
    163    public static FileTime ntfsTimeToFileTime(final long ntfsTime) {
    164        final long javaHundredsNanos = Math.addExact(ntfsTime, WINDOWS_EPOCH_OFFSET);
    165        final long javaSeconds = Math.floorDiv(javaHundredsNanos, HUNDRED_NANOS_PER_SECOND);
    166        final long javaNanos = Math.floorMod(javaHundredsNanos, HUNDRED_NANOS_PER_SECOND) * 100;
    167        return FileTime.from(Instant.ofEpochSecond(javaSeconds, javaNanos));
    168    }
    169
    170    /**
    171     * Adds milliseconds to a source FileTime.
    172     *
    173     * @param fileTime The source FileTime.
    174     * @param millisToAdd The milliseconds to add.
    175     * @return The resulting FileTime.
    176     */
    177    public static FileTime plusMillis(final FileTime fileTime, final long millisToAdd) {
    178        return FileTime.from(fileTime.toInstant().plusMillis(millisToAdd));
    179    }
    180
    181    /**
    182     * Adds nanoseconds from a source FileTime.
    183     *
    184     * @param fileTime The source FileTime.
    185     * @param nanosToSubtract The nanoseconds to subtract.
    186     * @return The resulting FileTime.
    187     */
    188    public static FileTime plusNanos(final FileTime fileTime, final long nanosToSubtract) {
    189        return FileTime.from(fileTime.toInstant().plusNanos(nanosToSubtract));
    190    }
    191
    192    /**
    193     * Adds seconds to a source FileTime.
    194     *
    195     * @param fileTime The source FileTime.
    196     * @param secondsToAdd The seconds to add.
    197     * @return The resulting FileTime.
    198     */
    199    public static FileTime plusSeconds(final FileTime fileTime, final long secondsToAdd) {
    200        return FileTime.from(fileTime.toInstant().plusSeconds(secondsToAdd));
    201    }
    202
    203    /**
    204     * Sets the last modified time of the given file path to now.
    205     *
    206     * @param path The file path to set.
    207     * @throws IOException if an I/O error occurs.
    208     */
    209    public static void setLastModifiedTime(final Path path) throws IOException {
    210        Files.setLastModifiedTime(path, now());
    211    }
    212
    213    /**
    214     * Converts {@link FileTime} to a {@link Date}. If the provided FileTime is {@code null}, the returned Date is also
    215     * {@code null}.
    216     *
    217     * @param fileTime the file time to be converted.
    218     * @return a {@link Date} which corresponds to the supplied time, or {@code null} if the time is {@code null}.
    219     * @see #toFileTime(Date)
    220     */
    221    public static Date toDate(final FileTime fileTime) {
    222        return fileTime != null ? new Date(fileTime.toMillis()) : null;
    223    }
    224
    225    /**
    226     * Converts {@link Date} to a {@link FileTime}. If the provided Date is {@code null}, the returned FileTime is also
    227     * {@code null}.
    228     *
    229     * @param date the date to be converted.
    230     * @return a {@link FileTime} which corresponds to the supplied date, or {@code null} if the date is {@code null}.
    231     * @see #toDate(FileTime)
    232     */
    233    public static FileTime toFileTime(final Date date) {
    234        return date != null ? FileTime.fromMillis(date.getTime()) : null;
    235    }
    236
    237    /**
    238     * Converts a {@link Date} to NTFS time.
    239     *
    240     * @param date the Date
    241     * @return the NTFS time
    242     */
    243    public static long toNtfsTime(final Date date) {
    244        final long javaHundredNanos = date.getTime() * HUNDRED_NANOS_PER_MILLISECOND;
    245        return Math.subtractExact(javaHundredNanos, WINDOWS_EPOCH_OFFSET);
    246    }
    247
    248    /**
    249     * Converts a {@link FileTime} to NTFS time (100-nanosecond units since 1 January 1601).
    250     *
    251     * @param fileTime the FileTime
    252     * @return the NTFS time in 100-nanosecond units
    253     */
    254    public static long toNtfsTime(final FileTime fileTime) {
    255        final Instant instant = fileTime.toInstant();
    256        final long javaHundredNanos = instant.getEpochSecond() * HUNDRED_NANOS_PER_SECOND + instant.getNano() / 100;
    257        return Math.subtractExact(javaHundredNanos, WINDOWS_EPOCH_OFFSET);
    258    }
    259
    260    /**
    261     * Converts Java time (milliseconds since Epoch) to NTFS time.
    262     *
    263     * @param javaTime the Java time
    264     * @return the NTFS time
    265     * @since 2.16.0
    266     */
    267    public static long toNtfsTime(final long javaTime) {
    268        final long javaHundredNanos = javaTime * HUNDRED_NANOS_PER_MILLISECOND;
    269        return Math.subtractExact(javaHundredNanos, WINDOWS_EPOCH_OFFSET);
    270    }
    271
    272    /**
    273     * Converts {@link FileTime} to standard UNIX time in seconds.
    274     * <p>
    275     * The returned seconds value may lie out of bounds of UNIX time. Check with {@link FileTimes#isUnixTime(long)}.
    276     * </p>
    277     *
    278     * @param fileTime the original FileTime.
    279     * @return the UNIX timestamp or 0 if the input is null.
    280     * @see #isUnixTime(long)
    281     * @since 2.16.0
    282     */
    283    public static long toUnixTime(final FileTime fileTime) {
    284        return fileTime != null ? fileTime.to(TimeUnit.SECONDS) : 0;
    285    }
    286
    287    private FileTimes() {
    288        // No instances.
    289    }
    290}