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.compress.utils;
019
020import java.nio.file.attribute.FileTime;
021import java.time.Instant;
022import java.util.Date;
023import java.util.concurrent.TimeUnit;
024
025/**
026 * Utility class for handling time-related types and conversions.
027 * <p>
028 * Understanding Unix vs NTFS timestamps:
029 * </p>
030 * <ul>
031 * <li>A <a href="https://en.wikipedia.org/wiki/Unix_time">Unix timestamp</a> is a primitive long starting at the Unix Epoch on January 1st, 1970 at Coordinated
032 * Universal Time (UTC)</li>
033 * <li>An <a href="https://learn.microsoft.com/en-us/windows/win32/sysinfo/file-times">NTFS timestamp</a> is a file time is a 64-bit value that represents the number
034 * of 100-nanosecond intervals that have elapsed since 12:00 A.M. January 1, 1601 Coordinated Universal Time (UTC).</li>
035 * </ul>
036 *
037 * @since 1.23
038 */
039public final class TimeUtils {
040
041    /** The amount of 100-nanosecond intervals in one millisecond. */
042    static final long HUNDRED_NANOS_PER_MILLISECOND = TimeUnit.MILLISECONDS.toNanos(1) / 100;
043
044    /** The amount of 100-nanosecond intervals in one second. */
045    static final long HUNDRED_NANOS_PER_SECOND = TimeUnit.SECONDS.toNanos(1) / 100;
046
047    /**
048     * <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms724290%28v=vs.85%29.aspx">Windows File Times</a>
049     * <p>
050     * 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
051     * Universal Time (UTC). This is the offset of Windows time 0 to Unix epoch in 100-nanosecond intervals.
052     * </p>
053     */
054    static final long WINDOWS_EPOCH_OFFSET = -116444736000000000L;
055
056    /**
057     * Tests whether a FileTime can be safely represented in the standard UNIX time.
058     *
059     * <p>
060     * TODO ? If the FileTime is null, this method always returns true.
061     * </p>
062     *
063     * @param time the FileTime to evaluate, can be null
064     * @return true if the time exceeds the minimum or maximum UNIX time, false otherwise
065     */
066    public static boolean isUnixTime(final FileTime time) {
067        return time == null ? true : isUnixTime(toUnixTime(time));
068    }
069
070    /**
071     * Tests whether a given number of seconds (since Epoch) can be safely represented in the standard UNIX time.
072     *
073     * @param seconds the number of seconds (since Epoch) to evaluate
074     * @return true if the time can be represented in the standard UNIX time, false otherwise
075     */
076    public static boolean isUnixTime(final long seconds) {
077        return Integer.MIN_VALUE <= seconds && seconds <= Integer.MAX_VALUE;
078    }
079
080    /**
081     * Converts NTFS time (100 nanosecond units since 1 January 1601) to Java time.
082     *
083     * @param ntfsTime the NTFS time in 100 nanosecond units
084     * @return the Date
085     */
086    public static Date ntfsTimeToDate(final long ntfsTime) {
087        final long javaHundredNanos = Math.addExact(ntfsTime, WINDOWS_EPOCH_OFFSET);
088        final long javaMillis = Math.floorDiv(javaHundredNanos, HUNDRED_NANOS_PER_MILLISECOND);
089        return new Date(javaMillis);
090    }
091
092    /**
093     * Converts NTFS time (100-nanosecond units since 1 January 1601) to a FileTime.
094     *
095     * @param ntfsTime the NTFS time in 100-nanosecond units
096     * @return the FileTime
097     *
098     * @see TimeUtils#WINDOWS_EPOCH_OFFSET
099     * @see TimeUtils#toNtfsTime(FileTime)
100     */
101    public static FileTime ntfsTimeToFileTime(final long ntfsTime) {
102        final long javaHundredsNanos = Math.addExact(ntfsTime, WINDOWS_EPOCH_OFFSET);
103        final long javaSeconds = Math.floorDiv(javaHundredsNanos, HUNDRED_NANOS_PER_SECOND);
104        final long javaNanos = Math.floorMod(javaHundredsNanos, HUNDRED_NANOS_PER_SECOND) * 100;
105        return FileTime.from(Instant.ofEpochSecond(javaSeconds, javaNanos));
106    }
107
108    /**
109     * Converts {@link FileTime} to a {@link Date}. If the provided FileTime is {@code null}, the returned Date is also {@code null}.
110     *
111     * @param fileTime the file time to be converted.
112     * @return a {@link Date} which corresponds to the supplied time, or {@code null} if the time is {@code null}.
113     * @see TimeUtils#toFileTime(Date)
114     */
115    public static Date toDate(final FileTime fileTime) {
116        return fileTime != null ? new Date(fileTime.toMillis()) : null;
117    }
118
119    /**
120     * Converts {@link Date} to a {@link FileTime}. If the provided Date is {@code null}, the returned FileTime is also {@code null}.
121     *
122     * @param date the date to be converted.
123     * @return a {@link FileTime} which corresponds to the supplied date, or {@code null} if the date is {@code null}.
124     * @see TimeUtils#toDate(FileTime)
125     */
126    public static FileTime toFileTime(final Date date) {
127        return date != null ? FileTime.fromMillis(date.getTime()) : null;
128    }
129
130    /**
131     * Converts a {@link Date} to NTFS time.
132     *
133     * @param date the Date
134     * @return the NTFS time
135     */
136    public static long toNtfsTime(final Date date) {
137        return toNtfsTime(date.getTime());
138    }
139
140    /**
141     * Converts a {@link FileTime} to NTFS time (100-nanosecond units since 1 January 1601).
142     *
143     * @param fileTime the FileTime
144     * @return the NTFS time in 100-nanosecond units
145     *
146     * @see TimeUtils#WINDOWS_EPOCH_OFFSET
147     * @see TimeUtils#ntfsTimeToFileTime(long)
148     */
149    public static long toNtfsTime(final FileTime fileTime) {
150        final Instant instant = fileTime.toInstant();
151        final long javaHundredNanos = (instant.getEpochSecond() * HUNDRED_NANOS_PER_SECOND) + (instant.getNano() / 100);
152        return Math.subtractExact(javaHundredNanos, WINDOWS_EPOCH_OFFSET);
153    }
154
155    /**
156     * Converts Java time (milliseconds since Epoch) to NTFS time.
157     *
158     * @param javaTime the Java time
159     * @return the NTFS time
160     */
161    public static long toNtfsTime(final long javaTime) {
162        final long javaHundredNanos = javaTime * HUNDRED_NANOS_PER_MILLISECOND;
163        return Math.subtractExact(javaHundredNanos, WINDOWS_EPOCH_OFFSET);
164    }
165
166    /**
167     * Converts {@link FileTime} to standard UNIX time.
168     *
169     * @param fileTime the original FileTime
170     * @return the UNIX timestamp
171     */
172    public static long toUnixTime(final FileTime fileTime) {
173        return fileTime.to(TimeUnit.SECONDS);
174    }
175
176    /**
177     * Truncates a FileTime to 100-nanosecond precision.
178     *
179     * @param fileTime the FileTime to be truncated
180     * @return the truncated FileTime
181     */
182    public static FileTime truncateToHundredNanos(final FileTime fileTime) {
183        final Instant instant = fileTime.toInstant();
184        return FileTime.from(Instant.ofEpochSecond(instant.getEpochSecond(), (instant.getNano() / 100) * 100));
185    }
186
187    /**
188     * Converts standard UNIX time (in seconds, UTC/GMT) to {@link FileTime}.
189     *
190     * @param time UNIX timestamp
191     * @return the corresponding FileTime
192     */
193    public static FileTime unixTimeToFileTime(final long time) {
194        return FileTime.from(time, TimeUnit.SECONDS);
195    }
196
197    /** Private constructor to prevent instantiation of this utility class. */
198    private TimeUtils() {
199    }
200}