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}