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    *      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 static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertNull;
22  import static org.junit.jupiter.api.Assertions.assertTrue;
23  
24  import java.nio.file.attribute.FileTime;
25  import java.time.Instant;
26  import java.util.Date;
27  import java.util.concurrent.TimeUnit;
28  import java.util.stream.Stream;
29  
30  import org.junit.jupiter.api.Test;
31  import org.junit.jupiter.params.ParameterizedTest;
32  import org.junit.jupiter.params.provider.Arguments;
33  import org.junit.jupiter.params.provider.MethodSource;
34  
35  /**
36   * Tests {@link FileTimes}.
37   */
38  class FileTimesTest {
39  
40      public static Stream<Arguments> fileTimeNanoUnitsToNtfsProvider() {
41          // @formatter:off
42          return Stream.of(
43              Arguments.of("1601-01-01T00:00:00.0000000Z", 0),
44              Arguments.of("1601-01-01T00:00:00.0000001Z", 1),
45              Arguments.of("1601-01-01T00:00:00.0000010Z", 10),
46              Arguments.of("1601-01-01T00:00:00.0000100Z", 100),
47              Arguments.of("1601-01-01T00:00:00.0001000Z", 1000),
48              Arguments.of("1600-12-31T23:59:59.9999999Z", -1),
49              Arguments.of("+30828-09-14T02:48:05.477580700Z", Long.MAX_VALUE),
50              Arguments.of("+30828-09-14T02:48:05.477580600Z", Long.MAX_VALUE - 1),
51              Arguments.of("+30828-09-14T02:48:05.477579700Z", Long.MAX_VALUE - 10),
52              Arguments.of("+30828-09-14T02:48:05.477570700Z", Long.MAX_VALUE - 100),
53              Arguments.of("+30828-09-14T02:48:05.477480700Z", Long.MAX_VALUE - 1000),
54              Arguments.of("-27627-04-19T21:11:54.522419200Z", Long.MIN_VALUE),
55              Arguments.of("-27627-04-19T21:11:54.522419300Z", Long.MIN_VALUE + 1),
56              Arguments.of("-27627-04-19T21:11:54.522420200Z", Long.MIN_VALUE + 10),
57              Arguments.of("-27627-04-19T21:11:54.522429200Z", Long.MIN_VALUE + 100),
58              Arguments.of("-27627-04-19T21:11:54.522519200Z", Long.MIN_VALUE + 1000),
59              Arguments.of("1601-01-01T00:00:00.0010000Z", FileTimes.HUNDRED_NANOS_PER_MILLISECOND),
60              Arguments.of("1601-01-01T00:00:00.0010001Z", FileTimes.HUNDRED_NANOS_PER_MILLISECOND + 1),
61              Arguments.of("1601-01-01T00:00:00.0009999Z", FileTimes.HUNDRED_NANOS_PER_MILLISECOND - 1),
62              Arguments.of("1600-12-31T23:59:59.9990000Z", -FileTimes.HUNDRED_NANOS_PER_MILLISECOND),
63              Arguments.of("1600-12-31T23:59:59.9990001Z", -FileTimes.HUNDRED_NANOS_PER_MILLISECOND + 1),
64              Arguments.of("1600-12-31T23:59:59.9989999Z", -FileTimes.HUNDRED_NANOS_PER_MILLISECOND - 1),
65              Arguments.of("1970-01-01T00:00:00.0000000Z", -FileTimes.UNIX_TO_NTFS_OFFSET),
66              Arguments.of("1970-01-01T00:00:00.0000001Z", -FileTimes.UNIX_TO_NTFS_OFFSET + 1),
67              Arguments.of("1970-01-01T00:00:00.0010000Z", -FileTimes.UNIX_TO_NTFS_OFFSET + FileTimes.HUNDRED_NANOS_PER_MILLISECOND),
68              Arguments.of("1969-12-31T23:59:59.9999999Z", -FileTimes.UNIX_TO_NTFS_OFFSET - 1),
69              Arguments.of("1969-12-31T23:59:59.9990000Z", -FileTimes.UNIX_TO_NTFS_OFFSET - FileTimes.HUNDRED_NANOS_PER_MILLISECOND));
70          // @formatter:on
71      }
72  
73      public static Stream<Arguments> fileTimeToNtfsProvider() {
74          // @formatter:off
75          return Stream.of(
76              Arguments.of("1970-01-01T00:00:00Z", FileTime.from(Instant.EPOCH)),
77              Arguments.of("1969-12-31T23:59:00Z", FileTime.from(Instant.EPOCH.minusSeconds(60))),
78              Arguments.of("1970-01-01T00:01:00Z", FileTime.from(Instant.EPOCH.plusSeconds(60))));
79          // @formatter:on
80      }
81  
82      public static Stream<Arguments> isUnixFileTimeProvider() {
83          // @formatter:off
84          return Stream.of(
85              Arguments.of("2022-12-27T12:45:22Z", true),
86              Arguments.of("2038-01-19T03:14:07Z", true),
87              Arguments.of("1901-12-13T23:14:08Z", true),
88              Arguments.of("1901-12-13T03:14:08Z", false),
89              Arguments.of("2038-01-19T03:14:08Z", false),
90              Arguments.of("2099-06-30T12:31:42Z", false));
91          // @formatter:on
92      }
93  
94      @ParameterizedTest
95      @MethodSource("fileTimeNanoUnitsToNtfsProvider")
96      void testDateToFileTime(final String instant, final long ignored) {
97          final Instant parsedInstant = Instant.parse(instant);
98          final FileTime parsedFileTime = FileTime.from(parsedInstant);
99          final Date parsedDate = Date.from(parsedInstant);
100         assertEquals(parsedFileTime.toMillis(), FileTimes.toFileTime(parsedDate).toMillis());
101     }
102 
103     @ParameterizedTest
104     @MethodSource("fileTimeNanoUnitsToNtfsProvider")
105     void testDateToNtfsTime(final String instantStr, final long ntfsTime) {
106         final long ntfsMillis = Math.floorDiv(ntfsTime, FileTimes.HUNDRED_NANOS_PER_MILLISECOND) * FileTimes.HUNDRED_NANOS_PER_MILLISECOND;
107         final Instant instant = Instant.parse(instantStr);
108         final Date parsed = Date.from(instant);
109         final long ntfsTime2 = FileTimes.toNtfsTime(parsed);
110         if (ntfsTime2 == Long.MIN_VALUE || ntfsTime2 == Long.MAX_VALUE) {
111             // toNtfsTime returns max long instead of overflowing
112         } else {
113             assertEquals(ntfsMillis, ntfsTime2);
114             assertEquals(ntfsMillis, FileTimes.toNtfsTime(parsed.getTime()));
115             assertEquals(ntfsMillis, FileTimes.toNtfsTime(FileTimes.ntfsTimeToInstant(ntfsTime).toEpochMilli()));
116         }
117         assertEquals(ntfsTime, FileTimes.toNtfsTime(FileTimes.ntfsTimeToInstant(ntfsTime)));
118     }
119 
120     @Test
121     void testEpoch() {
122         assertEquals(0, FileTimes.EPOCH.toMillis());
123     }
124 
125     @ParameterizedTest
126     @MethodSource("fileTimeNanoUnitsToNtfsProvider")
127     void testFileTimeToDate(final String instant, final long ignored) {
128         final Instant parsedInstant = Instant.parse(instant);
129         final FileTime parsedFileTime = FileTime.from(parsedInstant);
130         final Date parsedDate = Date.from(parsedInstant);
131         assertEquals(parsedDate, FileTimes.toDate(parsedFileTime));
132     }
133 
134     //@Disabled
135     @ParameterizedTest
136     @MethodSource("fileTimeToNtfsProvider")
137     void testFileTimeToNtfsTime(final String instantStr, final FileTime fileTime) {
138         final Instant instant = Instant.parse(instantStr);
139         final FileTime parsed = FileTime.from(instant);
140         assertEquals(instant, parsed.toInstant());
141         assertEquals(fileTime, FileTimes.ntfsTimeToFileTime(FileTimes.toNtfsTime(parsed)));
142     }
143 
144     @ParameterizedTest
145     @MethodSource("fileTimeNanoUnitsToNtfsProvider")
146     void testFileTimeToNtfsTime(final String instant, final long ntfsTime) {
147         final FileTime parsed = FileTime.from(Instant.parse(instant));
148         assertEquals(ntfsTime, FileTimes.toNtfsTime(parsed));
149     }
150 
151     @ParameterizedTest
152     @MethodSource("fileTimeNanoUnitsToNtfsProvider")
153     void testFromUnixTime(final String instant, final long ntfsTime) {
154         final long epochSecond = Instant.parse(instant).getEpochSecond();
155         assertEquals(epochSecond, FileTimes.fromUnixTime(epochSecond).to(TimeUnit.SECONDS));
156     }
157 
158     @ParameterizedTest
159     @MethodSource("isUnixFileTimeProvider")
160     void testIsUnixTime(final String instant, final boolean isUnixTime) {
161         assertEquals(isUnixTime, FileTimes.isUnixTime(FileTime.from(Instant.parse(instant))));
162     }
163 
164     void testIsUnixTimeFileTimeNull() {
165         assertTrue(FileTimes.isUnixTime(null));
166     }
167 
168     @ParameterizedTest
169     @MethodSource("isUnixFileTimeProvider")
170     void testIsUnixTimeLong(final String instant, final boolean isUnixTime) {
171         assertEquals(isUnixTime, FileTimes.isUnixTime(Instant.parse(instant).getEpochSecond()));
172     }
173 
174     @Test
175     void testMaxJavaTime() {
176         final long javaTime = Long.MAX_VALUE;
177         final Instant instant = Instant.ofEpochMilli(javaTime);
178         assertEquals(javaTime, instant.toEpochMilli()); // sanity check
179         final long ntfsTime = FileTimes.toNtfsTime(javaTime);
180         final Instant instant2 = FileTimes.ntfsTimeToInstant(ntfsTime);
181         if (ntfsTime == Long.MAX_VALUE) {
182             // toNtfsTime returns max long instead of overflowing
183         } else {
184             assertEquals(javaTime, instant2.toEpochMilli());
185         }
186     }
187 
188     @ParameterizedTest
189     @MethodSource("fileTimeNanoUnitsToNtfsProvider")
190     void testMaxJavaTimeParam(final String instantStr, final long javaTime) {
191         // final long javaTime = Long.MAX_VALUE;
192         final Instant instant = Instant.ofEpochMilli(javaTime);
193         assertEquals(javaTime, instant.toEpochMilli()); // sanity check
194         final long ntfsTime = FileTimes.toNtfsTime(javaTime);
195         final Instant instant2 = FileTimes.ntfsTimeToInstant(ntfsTime);
196         if (ntfsTime == Long.MIN_VALUE || ntfsTime == Long.MAX_VALUE) {
197             // toNtfsTime returns min or max long instead of overflowing
198         } else {
199             assertEquals(javaTime, instant2.toEpochMilli());
200         }
201     }
202 
203     @Test
204     void testMinusMillis() {
205         final int millis = 2;
206         assertEquals(Instant.EPOCH.minusMillis(millis), FileTimes.minusMillis(FileTimes.EPOCH, millis).toInstant());
207         assertEquals(Instant.EPOCH, FileTimes.minusMillis(FileTimes.EPOCH, 0).toInstant());
208     }
209 
210     @Test
211     void testMinusNanos() {
212         final int millis = 2;
213         assertEquals(Instant.EPOCH.minusNanos(millis), FileTimes.minusNanos(FileTimes.EPOCH, millis).toInstant());
214         assertEquals(Instant.EPOCH, FileTimes.minusNanos(FileTimes.EPOCH, 0).toInstant());
215     }
216 
217     @Test
218     void testMinusSeconds() {
219         final int seconds = 2;
220         assertEquals(Instant.EPOCH.minusSeconds(seconds), FileTimes.minusSeconds(FileTimes.EPOCH, seconds).toInstant());
221         assertEquals(Instant.EPOCH, FileTimes.minusSeconds(FileTimes.EPOCH, 0).toInstant());
222     }
223 
224     @ParameterizedTest
225     @MethodSource("fileTimeNanoUnitsToNtfsProvider")
226     void testNtfsTimeToDate(final String instant, final long ntfsTime) {
227         assertEquals(Instant.parse(instant).toEpochMilli(), FileTimes.ntfsTimeToDate(ntfsTime).toInstant().toEpochMilli());
228     }
229 
230     @ParameterizedTest
231     @MethodSource("fileTimeNanoUnitsToNtfsProvider")
232     void testNtfsTimeToFileTime(final String instantStr, final long ntfsTime) {
233         final Instant instant = Instant.parse(instantStr);
234         final FileTime fileTime = FileTime.from(instant);
235         assertEquals(instant, fileTime.toInstant()); // sanity check
236         assertEquals(instant, FileTimes.ntfsTimeToInstant(ntfsTime));
237         assertEquals(fileTime, FileTimes.ntfsTimeToFileTime(ntfsTime));
238     }
239 
240     @Test
241     void testNullDateToNullFileTime() {
242         assertNull(FileTimes.toFileTime(null));
243     }
244 
245     @Test
246     void testNullFileTimeToNullDate() {
247         assertNull(FileTimes.toDate(null));
248     }
249 
250     @Test
251     void testPlusMinusMillis() {
252         final int millis = 2;
253         assertEquals(Instant.EPOCH.plusMillis(millis), FileTimes.plusMillis(FileTimes.EPOCH, millis).toInstant());
254         assertEquals(Instant.EPOCH, FileTimes.plusMillis(FileTimes.EPOCH, 0).toInstant());
255     }
256 
257     @Test
258     void testPlusNanos() {
259         final int millis = 2;
260         assertEquals(Instant.EPOCH.plusNanos(millis), FileTimes.plusNanos(FileTimes.EPOCH, millis).toInstant());
261         assertEquals(Instant.EPOCH, FileTimes.plusNanos(FileTimes.EPOCH, 0).toInstant());
262     }
263 
264     @Test
265     void testPlusSeconds() {
266         final int seconds = 2;
267         assertEquals(Instant.EPOCH.plusSeconds(seconds), FileTimes.plusSeconds(FileTimes.EPOCH, seconds).toInstant());
268         assertEquals(Instant.EPOCH, FileTimes.plusSeconds(FileTimes.EPOCH, 0).toInstant());
269     }
270 
271     @ParameterizedTest
272     @MethodSource("isUnixFileTimeProvider")
273     void testToUnixTime(final String instant, final boolean isUnixTime) {
274         assertEquals(isUnixTime, FileTimes.isUnixTime(FileTimes.toUnixTime(FileTime.from(Instant.parse(instant)))));
275     }
276 }