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 * https://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 }