Path: blob/master/src/java.base/share/classes/java/nio/file/attribute/FileTime.java
41161 views
/*1* Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.nio.file.attribute;2627import java.time.Instant;28import java.time.LocalDateTime;29import java.time.ZoneOffset;30import java.util.Objects;31import java.util.concurrent.TimeUnit;3233/**34* Represents the value of a file's time stamp attribute. For example, it may35* represent the time that the file was last36* {@link BasicFileAttributes#lastModifiedTime() modified},37* {@link BasicFileAttributes#lastAccessTime() accessed},38* or {@link BasicFileAttributes#creationTime() created}.39*40* <p> Instances of this class are immutable.41*42* @since 1.743* @see java.nio.file.Files#setLastModifiedTime44* @see java.nio.file.Files#getLastModifiedTime45*/4647public final class FileTime48implements Comparable<FileTime>49{50/**51* The unit of granularity to interpret the value. Null if52* this {@code FileTime} is converted from an {@code Instant},53* the {@code value} and {@code unit} pair will not be used54* in this scenario.55*/56private final TimeUnit unit;5758/**59* The value since the epoch; can be negative.60*/61private final long value;6263/**64* The value as Instant (created lazily, if not from an instant)65*/66private Instant instant;6768/**69* The value return by toString (created lazily)70*/71private String valueAsString;7273/**74* Initializes a new instance of this class.75*/76private FileTime(long value, TimeUnit unit, Instant instant) {77this.value = value;78this.unit = unit;79this.instant = instant;80}8182/**83* Returns a {@code FileTime} representing a value at the given unit of84* granularity.85*86* @param value87* the value since the epoch (1970-01-01T00:00:00Z); can be88* negative89* @param unit90* the unit of granularity to interpret the value91*92* @return a {@code FileTime} representing the given value93*/94public static FileTime from(long value, TimeUnit unit) {95Objects.requireNonNull(unit, "unit");96return new FileTime(value, unit, null);97}9899/**100* Returns a {@code FileTime} representing the given value in milliseconds.101*102* @param value103* the value, in milliseconds, since the epoch104* (1970-01-01T00:00:00Z); can be negative105*106* @return a {@code FileTime} representing the given value107*/108public static FileTime fromMillis(long value) {109return new FileTime(value, TimeUnit.MILLISECONDS, null);110}111112/**113* Returns a {@code FileTime} representing the same point of time value114* on the time-line as the provided {@code Instant} object.115*116* @param instant117* the instant to convert118* @return a {@code FileTime} representing the same point on the time-line119* as the provided instant120* @since 1.8121*/122public static FileTime from(Instant instant) {123Objects.requireNonNull(instant, "instant");124return new FileTime(0, null, instant);125}126127/**128* Returns the value at the given unit of granularity.129*130* <p> Conversion from a coarser granularity that would numerically overflow131* saturate to {@code Long.MIN_VALUE} if negative or {@code Long.MAX_VALUE}132* if positive.133*134* @param unit135* the unit of granularity for the return value136*137* @return value in the given unit of granularity, since the epoch138* since the epoch (1970-01-01T00:00:00Z); can be negative139*/140public long to(TimeUnit unit) {141Objects.requireNonNull(unit, "unit");142if (this.unit != null) {143return unit.convert(this.value, this.unit);144} else {145long secs = unit.convert(instant.getEpochSecond(), TimeUnit.SECONDS);146if (secs == Long.MIN_VALUE || secs == Long.MAX_VALUE) {147return secs;148}149long nanos = unit.convert(instant.getNano(), TimeUnit.NANOSECONDS);150long r = secs + nanos;151// Math.addExact() variant152if (((secs ^ r) & (nanos ^ r)) < 0) {153return (secs < 0) ? Long.MIN_VALUE : Long.MAX_VALUE;154}155return r;156}157}158159/**160* Returns the value in milliseconds.161*162* <p> Conversion from a coarser granularity that would numerically overflow163* saturate to {@code Long.MIN_VALUE} if negative or {@code Long.MAX_VALUE}164* if positive.165*166* @return the value in milliseconds, since the epoch (1970-01-01T00:00:00Z)167*/168public long toMillis() {169if (unit != null) {170return unit.toMillis(value);171} else {172long secs = instant.getEpochSecond();173int nanos = instant.getNano();174// Math.multiplyExact() variant175long r = secs * 1000;176long ax = Math.abs(secs);177if (((ax | 1000) >>> 31 != 0)) {178if ((r / 1000) != secs) {179return (secs < 0) ? Long.MIN_VALUE : Long.MAX_VALUE;180}181}182return r + nanos / 1000_000;183}184}185186/**187* Time unit constants for conversion.188*/189private static final long HOURS_PER_DAY = 24L;190private static final long MINUTES_PER_HOUR = 60L;191private static final long SECONDS_PER_MINUTE = 60L;192private static final long SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;193private static final long SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY;194private static final long MILLIS_PER_SECOND = 1000L;195private static final long MICROS_PER_SECOND = 1000_000L;196private static final long NANOS_PER_SECOND = 1000_000_000L;197private static final int NANOS_PER_MILLI = 1000_000;198private static final int NANOS_PER_MICRO = 1000;199// The epoch second of Instant.MIN.200private static final long MIN_SECOND = -31557014167219200L;201// The epoch second of Instant.MAX.202private static final long MAX_SECOND = 31556889864403199L;203204/*205* Scale d by m, checking for overflow.206*/207private static long scale(long d, long m, long over) {208if (d > over) return Long.MAX_VALUE;209if (d < -over) return Long.MIN_VALUE;210return d * m;211}212213/**214* Converts this {@code FileTime} object to an {@code Instant}.215*216* <p> The conversion creates an {@code Instant} that represents the217* same point on the time-line as this {@code FileTime}.218*219* <p> {@code FileTime} can store points on the time-line further in the220* future and further in the past than {@code Instant}. Conversion221* from such further time points saturates to {@link Instant#MIN} if222* earlier than {@code Instant.MIN} or {@link Instant#MAX} if later223* than {@code Instant.MAX}.224*225* @return an instant representing the same point on the time-line as226* this {@code FileTime} object227* @since 1.8228*/229public Instant toInstant() {230if (instant == null) {231long secs = 0L;232int nanos = 0;233switch (unit) {234case DAYS:235secs = scale(value, SECONDS_PER_DAY,236Long.MAX_VALUE/SECONDS_PER_DAY);237break;238case HOURS:239secs = scale(value, SECONDS_PER_HOUR,240Long.MAX_VALUE/SECONDS_PER_HOUR);241break;242case MINUTES:243secs = scale(value, SECONDS_PER_MINUTE,244Long.MAX_VALUE/SECONDS_PER_MINUTE);245break;246case SECONDS:247secs = value;248break;249case MILLISECONDS:250secs = Math.floorDiv(value, MILLIS_PER_SECOND);251nanos = (int)Math.floorMod(value, MILLIS_PER_SECOND)252* NANOS_PER_MILLI;253break;254case MICROSECONDS:255secs = Math.floorDiv(value, MICROS_PER_SECOND);256nanos = (int)Math.floorMod(value, MICROS_PER_SECOND)257* NANOS_PER_MICRO;258break;259case NANOSECONDS:260secs = Math.floorDiv(value, NANOS_PER_SECOND);261nanos = (int)Math.floorMod(value, NANOS_PER_SECOND);262break;263default : throw new AssertionError("Unit not handled");264}265if (secs <= MIN_SECOND)266instant = Instant.MIN;267else if (secs >= MAX_SECOND)268instant = Instant.MAX;269else270instant = Instant.ofEpochSecond(secs, nanos);271}272return instant;273}274275/**276* Tests this {@code FileTime} for equality with the given object.277*278* <p> The result is {@code true} if and only if the argument is not {@code279* null} and is a {@code FileTime} that represents the same time. This280* method satisfies the general contract of the {@code Object.equals} method.281*282* @param obj283* the object to compare with284*285* @return {@code true} if, and only if, the given object is a {@code286* FileTime} that represents the same time287*/288@Override289public boolean equals(Object obj) {290return (obj instanceof FileTime) ? compareTo((FileTime)obj) == 0 : false;291}292293/**294* Computes a hash code for this file time.295*296* <p> The hash code is based upon the value represented, and satisfies the297* general contract of the {@link Object#hashCode} method.298*299* @return the hash-code value300*/301@Override302public int hashCode() {303// hashcode of instant representation to satisfy contract with equals304return toInstant().hashCode();305}306307private long toDays() {308if (unit != null) {309return unit.toDays(value);310} else {311return TimeUnit.SECONDS.toDays(toInstant().getEpochSecond());312}313}314315private long toExcessNanos(long days) {316if (unit != null) {317return unit.toNanos(value - unit.convert(days, TimeUnit.DAYS));318} else {319return TimeUnit.SECONDS.toNanos(toInstant().getEpochSecond()320- TimeUnit.DAYS.toSeconds(days));321}322}323324/**325* Compares the value of two {@code FileTime} objects for order.326*327* @param other328* the other {@code FileTime} to be compared329*330* @return {@code 0} if this {@code FileTime} is equal to {@code other}, a331* value less than 0 if this {@code FileTime} represents a time332* that is before {@code other}, and a value greater than 0 if this333* {@code FileTime} represents a time that is after {@code other}334*/335@Override336public int compareTo(FileTime other) {337// same granularity338if (unit != null && unit == other.unit) {339return Long.compare(value, other.value);340} else {341// compare using instant representation when unit differs342long secs = toInstant().getEpochSecond();343long secsOther = other.toInstant().getEpochSecond();344int cmp = Long.compare(secs, secsOther);345if (cmp != 0) {346return cmp;347}348cmp = Long.compare(toInstant().getNano(), other.toInstant().getNano());349if (cmp != 0) {350return cmp;351}352if (secs != MAX_SECOND && secs != MIN_SECOND) {353return 0;354}355// if both this and other's Instant reps are MIN/MAX,356// use daysSinceEpoch and nanosOfDays, which will not357// saturate during calculation.358long days = toDays();359long daysOther = other.toDays();360if (days == daysOther) {361return Long.compare(toExcessNanos(days), other.toExcessNanos(daysOther));362}363return Long.compare(days, daysOther);364}365}366367// days in a 400 year cycle = 146097368// days in a 10,000 year cycle = 146097 * 25369// seconds per day = 86400370private static final long DAYS_PER_10000_YEARS = 146097L * 25L;371private static final long SECONDS_PER_10000_YEARS = 146097L * 25L * 86400L;372private static final long SECONDS_0000_TO_1970 = ((146097L * 5L) - (30L * 365L + 7L)) * 86400L;373374// append year/month/day/hour/minute/second/nano with width and 0 padding375private StringBuilder append(StringBuilder sb, int w, int d) {376while (w > 0) {377sb.append((char)(d/w + '0'));378d = d % w;379w /= 10;380}381return sb;382}383384/**385* Returns the string representation of this {@code FileTime}. The string386* is returned in the <a387* href="http://www.w3.org/TR/NOTE-datetime">ISO 8601</a> format:388* <pre>389* YYYY-MM-DDThh:mm:ss[.s+]Z390* </pre>391* where "{@code [.s+]}" represents a dot followed by one of more digits392* for the decimal fraction of a second. It is only present when the decimal393* fraction of a second is not zero. For example, {@code394* FileTime.fromMillis(1234567890000L).toString()} yields {@code395* "2009-02-13T23:31:30Z"}, and {@code FileTime.fromMillis(1234567890123L).toString()}396* yields {@code "2009-02-13T23:31:30.123Z"}.397*398* <p> A {@code FileTime} is primarily intended to represent the value of a399* file's time stamp. Where used to represent <i>extreme values</i>, where400* the year is less than "{@code 0001}" or greater than "{@code 9999}" then401* this method deviates from ISO 8601 in the same manner as the402* <a href="http://www.w3.org/TR/xmlschema-2/#deviantformats">XML Schema403* language</a>. That is, the year may be expanded to more than four digits404* and may be negative-signed. If more than four digits then leading zeros405* are not present. The year before "{@code 0001}" is "{@code -0001}".406*407* @return the string representation of this file time408*/409@Override410public String toString() {411if (valueAsString == null) {412long secs = 0L;413int nanos = 0;414if (instant == null && unit.compareTo(TimeUnit.SECONDS) >= 0) {415secs = unit.toSeconds(value);416} else {417secs = toInstant().getEpochSecond();418nanos = toInstant().getNano();419}420LocalDateTime ldt;421int year = 0;422if (secs >= -SECONDS_0000_TO_1970) {423// current era424long zeroSecs = secs - SECONDS_PER_10000_YEARS + SECONDS_0000_TO_1970;425long hi = Math.floorDiv(zeroSecs, SECONDS_PER_10000_YEARS) + 1;426long lo = Math.floorMod(zeroSecs, SECONDS_PER_10000_YEARS);427ldt = LocalDateTime.ofEpochSecond(lo - SECONDS_0000_TO_1970, nanos, ZoneOffset.UTC);428year = ldt.getYear() + (int)hi * 10000;429} else {430// before current era431long zeroSecs = secs + SECONDS_0000_TO_1970;432long hi = zeroSecs / SECONDS_PER_10000_YEARS;433long lo = zeroSecs % SECONDS_PER_10000_YEARS;434ldt = LocalDateTime.ofEpochSecond(lo - SECONDS_0000_TO_1970, nanos, ZoneOffset.UTC);435year = ldt.getYear() + (int)hi * 10000;436}437if (year <= 0) {438year = year - 1;439}440int fraction = ldt.getNano();441StringBuilder sb = new StringBuilder(64);442sb.append(year < 0 ? "-" : "");443year = Math.abs(year);444if (year < 10000) {445append(sb, 1000, Math.abs(year));446} else {447sb.append(String.valueOf(year));448}449sb.append('-');450append(sb, 10, ldt.getMonthValue());451sb.append('-');452append(sb, 10, ldt.getDayOfMonth());453sb.append('T');454append(sb, 10, ldt.getHour());455sb.append(':');456append(sb, 10, ldt.getMinute());457sb.append(':');458append(sb, 10, ldt.getSecond());459if (fraction != 0) {460sb.append('.');461// adding leading zeros and stripping any trailing zeros462int w = 100_000_000;463while (fraction % 10 == 0) {464fraction /= 10;465w /= 10;466}467append(sb, w, fraction);468}469sb.append('Z');470valueAsString = sb.toString();471}472return valueAsString;473}474}475476477