View Javadoc
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  
18  package org.apache.commons.io.file.attribute;
19  
20  import java.io.IOException;
21  import java.math.BigDecimal;
22  import java.math.MathContext;
23  import java.math.RoundingMode;
24  import java.nio.file.Files;
25  import java.nio.file.Path;
26  import java.nio.file.attribute.FileTime;
27  import java.time.Instant;
28  import java.util.Date;
29  import java.util.concurrent.TimeUnit;
30  
31  /**
32   * Helps use {@link FileTime} and interoperate Date and NTFS times.
33   * <p>
34   * 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
35   * Universal Time (UTC). This is the offset of Windows time 0 to Unix epoch in 100-nanosecond intervals.
36   * </p>
37   *
38   * @since 2.12.0
39   * @see <a href="https://learn.microsoft.com/en-us/windows/win32/sysinfo/file-times">NTFS File Times</a>
40   */
41  public final class FileTimes {
42  
43      private static final BigDecimal LONG_MIN_VALUE_BD = BigDecimal.valueOf(Long.MIN_VALUE);
44  
45      private static final BigDecimal LONG_MAX_VALUE_BD = BigDecimal.valueOf(Long.MAX_VALUE);
46  
47      private static final MathContext MATH_CONTEXT = new MathContext(0, RoundingMode.FLOOR);
48  
49      /**
50       * Constant for the {@code 1970-01-01T00:00:00Z} {@link Instant#EPOCH epoch} as a time stamp attribute.
51       *
52       * @see Instant#EPOCH
53       */
54      public static final FileTime EPOCH = FileTime.from(Instant.EPOCH);
55  
56      /**
57       * The offset of Windows time 0 to Unix epoch in 100-nanosecond intervals.
58       *
59       * <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms724290%28v=vs.85%29.aspx">Windows File Times</a>
60       * <p>
61       * 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
62       * Universal Time (UTC). This is the offset of Windows time 0 to Unix epoch in 100-nanosecond intervals.
63       * </p>
64       */
65      static final long UNIX_TO_NTFS_OFFSET = -116444736000000000L;
66  
67      private static final BigDecimal UNIX_TO_NTFS_OFFSET_BD = BigDecimal.valueOf(UNIX_TO_NTFS_OFFSET);
68  
69      /**
70       * The amount of 100-nanosecond intervals in one second.
71       */
72      private static final long HUNDRED_NANOS_PER_SECOND = TimeUnit.SECONDS.toNanos(1) / 100;
73  
74      private static final BigDecimal HUNDRED_NANOS_PER_SECOND_BD = BigDecimal.valueOf(HUNDRED_NANOS_PER_SECOND);
75  
76      /**
77       * The amount of 100-nanosecond intervals in one millisecond.
78       */
79      static final long HUNDRED_NANOS_PER_MILLISECOND = TimeUnit.MILLISECONDS.toNanos(1) / 100;
80  
81      static final BigDecimal HUNDRED_NANOS_PER_MILLISECOND_BD = BigDecimal.valueOf(HUNDRED_NANOS_PER_MILLISECOND);
82  
83      private static final long HUNDRED = 100L;
84  
85      private static final BigDecimal HUNDRED_BD = BigDecimal.valueOf(HUNDRED);
86  
87      /**
88       * Converts standard Unix time (in seconds, UTC/GMT) to {@link FileTime}.
89       *
90       * @param time Unix timestamp (seconds).
91       * @return the corresponding FileTime.
92       * @since 2.16.0
93       */
94      public static FileTime fromUnixTime(final long time) {
95          return FileTime.from(time, TimeUnit.SECONDS);
96      }
97  
98      /**
99       * Tests whether a FileTime can be safely represented in the standard Unix time.
100      * <p>
101      * If the FileTime is null, this method returns true.
102      * </p>
103      *
104      * @param time the FileTime to evaluate, can be null.
105      * @return true if the time exceeds the minimum or maximum Unix time, false otherwise.
106      * @since 2.16.0
107      */
108     public static boolean isUnixTime(final FileTime time) {
109         return isUnixTime(toUnixTime(time));
110     }
111 
112     /**
113      * Tests whether a given number of seconds (since Epoch) can be safely represented in the standard Unix time.
114      *
115      * @param seconds the number of seconds (since Epoch) to evaluate.
116      * @return true if the time can be represented in the standard Unix time, false otherwise.
117      * @since 2.16.0
118      */
119     public static boolean isUnixTime(final long seconds) {
120         return Integer.MIN_VALUE <= seconds && seconds <= Integer.MAX_VALUE;
121     }
122 
123     /**
124      * Subtracts milliseconds from a source FileTime.
125      *
126      * @param fileTime         The source FileTime.
127      * @param millisToSubtract The milliseconds to subtract.
128      * @return The resulting FileTime.
129      */
130     public static FileTime minusMillis(final FileTime fileTime, final long millisToSubtract) {
131         return FileTime.from(fileTime.toInstant().minusMillis(millisToSubtract));
132     }
133 
134     /**
135      * Subtracts nanoseconds from a source FileTime.
136      *
137      * @param fileTime        The source FileTime.
138      * @param nanosToSubtract The nanoseconds to subtract.
139      * @return The resulting FileTime.
140      */
141     public static FileTime minusNanos(final FileTime fileTime, final long nanosToSubtract) {
142         return FileTime.from(fileTime.toInstant().minusNanos(nanosToSubtract));
143     }
144 
145     /**
146      * Subtracts seconds from a source FileTime.
147      *
148      * @param fileTime          The source FileTime.
149      * @param secondsToSubtract The seconds to subtract.
150      * @return The resulting FileTime.
151      */
152     public static FileTime minusSeconds(final FileTime fileTime, final long secondsToSubtract) {
153         return FileTime.from(fileTime.toInstant().minusSeconds(secondsToSubtract));
154     }
155 
156     /**
157      * Obtains the current instant FileTime from the system clock.
158      *
159      * @return the current instant FileTime from the system clock.
160      */
161     public static FileTime now() {
162         return FileTime.from(Instant.now());
163     }
164 
165     static Date ntfsTimeToDate(final BigDecimal ntfsTime) {
166         return new Date(ntfsTimeToInstant(ntfsTime).toEpochMilli());
167     }
168 
169     /**
170      * Converts an NTFS time (100 nanosecond units since 1 January 1601) to a {@link Date}.
171      * <p>
172      * 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).
173      * </p>
174      *
175      * @param ntfsTime the NTFS time, 100-nanosecond units since 1 January 1601.
176      * @return the Date input.
177      * @see <a href="https://learn.microsoft.com/en-us/windows/win32/sysinfo/file-times">NTFS File Times</a>
178      */
179     public static Date ntfsTimeToDate(final long ntfsTime) {
180         return ntfsTimeToDate(BigDecimal.valueOf(ntfsTime));
181     }
182 
183     /**
184      * Converts an NTFS time (100-nanosecond units since 1 January 1601) to a {@link FileTime}.
185      * <p>
186      * 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).
187      * </p>
188      *
189      * @param ntfsTime the NTFS time, 100-nanosecond units since 1 January 1601.
190      * @return the FileTime input.
191      * @see #toNtfsTime(FileTime)
192      * @see <a href="https://learn.microsoft.com/en-us/windows/win32/sysinfo/file-times">NTFS File Times</a>
193      */
194     public static FileTime ntfsTimeToFileTime(final long ntfsTime) {
195         return FileTime.from(ntfsTimeToInstant(ntfsTime));
196     }
197 
198     static Instant ntfsTimeToInstant(final BigDecimal ntfsTime) {
199         final BigDecimal javaHundredsNanos = ntfsTime.add(UNIX_TO_NTFS_OFFSET_BD);
200         final BigDecimal[] dar = javaHundredsNanos.divideAndRemainder(HUNDRED_NANOS_PER_SECOND_BD, MATH_CONTEXT);
201         return Instant.ofEpochSecond(dar[0].longValueExact(), dar[1].multiply(HUNDRED_BD).longValueExact());
202     }
203 
204     static Instant ntfsTimeToInstant(final long ntfsTime) {
205         return ntfsTimeToInstant(BigDecimal.valueOf(ntfsTime));
206     }
207 
208     /**
209      * Adds milliseconds to a {@link FileTime}.
210      *
211      * @param fileTime    The source FileTime.
212      * @param millisToAdd The milliseconds to add.
213      * @return The resulting FileTime.
214      */
215     public static FileTime plusMillis(final FileTime fileTime, final long millisToAdd) {
216         return FileTime.from(fileTime.toInstant().plusMillis(millisToAdd));
217     }
218 
219     /**
220      * Adds nanoseconds to a {@link FileTime}.
221      *
222      * @param fileTime        The source FileTime.
223      * @param nanosToSubtract The nanoseconds to subtract.
224      * @return The resulting FileTime.
225      */
226     public static FileTime plusNanos(final FileTime fileTime, final long nanosToSubtract) {
227         return FileTime.from(fileTime.toInstant().plusNanos(nanosToSubtract));
228     }
229 
230     /**
231      * Adds seconds to a {@link FileTime}.
232      *
233      * @param fileTime     The source FileTime.
234      * @param secondsToAdd The seconds to add.
235      * @return The resulting FileTime.
236      */
237     public static FileTime plusSeconds(final FileTime fileTime, final long secondsToAdd) {
238         return FileTime.from(fileTime.toInstant().plusSeconds(secondsToAdd));
239     }
240 
241     /**
242      * Sets the last modified time of the given file path to now.
243      *
244      * @param path The file path to set.
245      * @throws IOException if an I/O error occurs.
246      */
247     public static void setLastModifiedTime(final Path path) throws IOException {
248         Files.setLastModifiedTime(path, now());
249     }
250 
251     /**
252      * Converts a {@link FileTime} to a {@link Date}. If the provided FileTime is {@code null}, the returned Date is also {@code null}.
253      *
254      * @param fileTime the file time to be converted.
255      * @return a {@link Date} which corresponds to the supplied time, or {@code null} if the time is {@code null}.
256      * @see #toFileTime(Date)
257      */
258     public static Date toDate(final FileTime fileTime) {
259         return fileTime != null ? new Date(fileTime.toMillis()) : null;
260     }
261 
262     /**
263      * Converts a {@link Date} to a {@link FileTime}. If the provided Date is {@code null}, the returned FileTime is also {@code null}.
264      *
265      * @param date the date to be converted.
266      * @return a {@link FileTime} which corresponds to the supplied date, or {@code null} if the date is {@code null}.
267      * @see #toDate(FileTime)
268      */
269     public static FileTime toFileTime(final Date date) {
270         return date != null ? FileTime.fromMillis(date.getTime()) : null;
271     }
272 
273     /**
274      * Converts a {@link Date} to NTFS time.
275      * <p>
276      * 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).
277      * </p>
278      *
279      * @param date the Date input.
280      * @return the NTFS time, 100-nanosecond units since 1 January 1601.
281      */
282     public static long toNtfsTime(final Date date) {
283         return toNtfsTime(date.getTime());
284     }
285 
286     /**
287      * Converts a {@link FileTime} to NTFS time (100-nanosecond units since 1 January 1601).
288      * <p>
289      * 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).
290      * </p>
291      *
292      * @param fileTime the FileTime input.
293      * @return the NTFS time, 100-nanosecond units since 1 January 1601.
294      */
295     public static long toNtfsTime(final FileTime fileTime) {
296         return toNtfsTime(fileTime.toInstant());
297     }
298 
299     static long toNtfsTime(final Instant instant) {
300         final BigDecimal javaHundredNanos = BigDecimal.valueOf(instant.getEpochSecond()).multiply(HUNDRED_NANOS_PER_SECOND_BD)
301                 .add(BigDecimal.valueOf(instant.getNano() / 100));
302         return javaHundredNanos.subtract(UNIX_TO_NTFS_OFFSET_BD).longValueExact();
303     }
304 
305     /**
306      * Converts a Java time (milliseconds since Epoch) to NTFS time.
307      * <p>
308      * 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).
309      * </p>
310      *
311      * @param javaTime the Java time
312      * @return the NTFS time, 100-nanosecond units since 1 January 1601.
313      * @since 2.16.0
314      */
315     public static long toNtfsTime(final long javaTime) {
316         final BigDecimal javaHundredNanos = BigDecimal.valueOf(javaTime).multiply(HUNDRED_NANOS_PER_MILLISECOND_BD);
317         final BigDecimal ntfsTime = javaHundredNanos.subtract(UNIX_TO_NTFS_OFFSET_BD);
318         if (ntfsTime.compareTo(LONG_MAX_VALUE_BD) >= 0) {
319             return Long.MAX_VALUE;
320         }
321         if (ntfsTime.compareTo(LONG_MIN_VALUE_BD) <= 0) {
322             return Long.MIN_VALUE;
323         }
324         return ntfsTime.longValue();
325     }
326 
327     /**
328      * Converts a {@link FileTime} to standard Unix time in seconds.
329      * <p>
330      * The returned seconds value may lie out of bounds of Unix time. Check with {@link FileTimes#isUnixTime(long)}.
331      * </p>
332      *
333      * @param fileTime the original FileTime.
334      * @return the Unix timestamp or 0 if the input is null.
335      * @see #isUnixTime(long)
336      * @since 2.16.0
337      */
338     public static long toUnixTime(final FileTime fileTime) {
339         return fileTime != null ? fileTime.to(TimeUnit.SECONDS) : 0;
340     }
341 
342     private FileTimes() {
343         // No instances.
344     }
345 }