提交 bb0bb6cf 编写于 作者: S sherman

8011647: Add java.time.Instant methods to java.nio.file.attribute.FileTime

Summary: added the toInstant()/from(Instant) to FileTime
Reviewed-by: alanb
上级 fd01660c
...@@ -25,12 +25,10 @@ ...@@ -25,12 +25,10 @@
package java.nio.file.attribute; package java.nio.file.attribute;
import java.util.Calendar; import java.time.Instant;
import java.util.GregorianCalendar; import java.time.LocalDateTime;
import java.util.Date; import java.time.ZoneOffset;
import java.util.Formatter; import java.util.Objects;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
...@@ -51,42 +49,35 @@ public final class FileTime ...@@ -51,42 +49,35 @@ public final class FileTime
implements Comparable<FileTime> implements Comparable<FileTime>
{ {
/** /**
* The value since the epoch; can be negative. * The unit of granularity to interpret the value. Null if
*/ * this {@code FileTime} is converted from an {@code Instant},
private final long value; * the {@code value} and {@code unit} pair will not be used
* in this scenario.
/**
* The unit of granularity to interpret the value.
*/ */
private final TimeUnit unit; private final TimeUnit unit;
/** /**
* The value return by toString (created lazily) * The value since the epoch; can be negative.
*/ */
private String valueAsString; private final long value;
/** /**
* The value in days and excess nanos (created lazily) * The value as Instant (created lazily, if not from an instant)
*/ */
private DaysAndNanos daysAndNanos; private Instant instant;
/** /**
* Returns a DaysAndNanos object representing the value. * The value return by toString (created lazily)
*/ */
private DaysAndNanos asDaysAndNanos() { private String valueAsString;
if (daysAndNanos == null)
daysAndNanos = new DaysAndNanos(value, unit);
return daysAndNanos;
}
/** /**
* Initializes a new instance of this class. * Initializes a new instance of this class.
*/ */
private FileTime(long value, TimeUnit unit) { private FileTime(long value, TimeUnit unit, Instant instant) {
if (unit == null)
throw new NullPointerException();
this.value = value; this.value = value;
this.unit = unit; this.unit = unit;
this.instant = instant;
} }
/** /**
...@@ -102,7 +93,8 @@ public final class FileTime ...@@ -102,7 +93,8 @@ public final class FileTime
* @return a {@code FileTime} representing the given value * @return a {@code FileTime} representing the given value
*/ */
public static FileTime from(long value, TimeUnit unit) { public static FileTime from(long value, TimeUnit unit) {
return new FileTime(value, unit); Objects.requireNonNull(unit, "unit");
return new FileTime(value, unit, null);
} }
/** /**
...@@ -115,7 +107,22 @@ public final class FileTime ...@@ -115,7 +107,22 @@ public final class FileTime
* @return a {@code FileTime} representing the given value * @return a {@code FileTime} representing the given value
*/ */
public static FileTime fromMillis(long value) { public static FileTime fromMillis(long value) {
return new FileTime(value, TimeUnit.MILLISECONDS); return new FileTime(value, TimeUnit.MILLISECONDS, null);
}
/**
* Returns a {@code FileTime} representing the same point of time value
* on the time-line as the provided {@code Instant} object.
*
* @param instant
* the instant to convert
* @return a {@code FileTime} representing the same point on the time-line
* as the provided instant
* @since 1.8
*/
public static FileTime from(Instant instant) {
Objects.requireNonNull(instant, "instant");
return new FileTime(0, null, instant);
} }
/** /**
...@@ -132,7 +139,22 @@ public final class FileTime ...@@ -132,7 +139,22 @@ public final class FileTime
* since the epoch (1970-01-01T00:00:00Z); can be negative * since the epoch (1970-01-01T00:00:00Z); can be negative
*/ */
public long to(TimeUnit unit) { public long to(TimeUnit unit) {
return unit.convert(this.value, this.unit); Objects.requireNonNull(unit, "unit");
if (this.unit != null) {
return unit.convert(this.value, this.unit);
} else {
long secs = unit.convert(instant.getEpochSecond(), TimeUnit.SECONDS);
if (secs == Long.MIN_VALUE || secs == Long.MAX_VALUE) {
return secs;
}
long nanos = unit.convert(instant.getNano(), TimeUnit.NANOSECONDS);
long r = secs + nanos;
// Math.addExact() variant
if (((secs ^ r) & (nanos ^ r)) < 0) {
return (secs < 0) ? Long.MIN_VALUE : Long.MAX_VALUE;
}
return r;
}
} }
/** /**
...@@ -145,7 +167,110 @@ public final class FileTime ...@@ -145,7 +167,110 @@ public final class FileTime
* @return the value in milliseconds, since the epoch (1970-01-01T00:00:00Z) * @return the value in milliseconds, since the epoch (1970-01-01T00:00:00Z)
*/ */
public long toMillis() { public long toMillis() {
return unit.toMillis(value); if (unit != null) {
return unit.toMillis(value);
} else {
long secs = instant.getEpochSecond();
int nanos = instant.getNano();
// Math.multiplyExact() variant
long r = secs * 1000;
long ax = Math.abs(secs);
if (((ax | 1000) >>> 31 != 0)) {
if ((r / 1000) != secs) {
return (secs < 0) ? Long.MIN_VALUE : Long.MAX_VALUE;
}
}
return r + nanos / 1000_000;
}
}
/**
* Time unit constants for conversion.
*/
private static final long HOURS_PER_DAY = 24L;
private static final long MINUTES_PER_HOUR = 60L;
private static final long SECONDS_PER_MINUTE = 60L;
private static final long SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;
private static final long SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY;
private static final long MILLIS_PER_SECOND = 1000L;
private static final long MICROS_PER_SECOND = 1000_000L;
private static final long NANOS_PER_SECOND = 1000_000_000L;
private static final int NANOS_PER_MILLI = 1000_000;
private static final int NANOS_PER_MICRO = 1000;
// The epoch second of Instant.MIN.
private static final long MIN_SECOND = -31557014167219200L;
// The epoch second of Instant.MAX.
private static final long MAX_SECOND = 31556889864403199L;
/*
* Scale d by m, checking for overflow.
*/
private static long scale(long d, long m, long over) {
if (d > over) return Long.MAX_VALUE;
if (d < -over) return Long.MIN_VALUE;
return d * m;
}
/**
* Converts this {@code FileTime} object to an {@code Instant}.
*
* <p> The conversion creates an {@code Instant} that represents the
* same point on the time-line as this {@code FileTime}.
*
* <p> {@code FileTime} can store points on the time-line further in the
* future and further in the past than {@code Instant}. Conversion
* from such further time points saturates to {@link Instant.MIN} if
* earlier than {@code Instant.MIN} or {@link Instant.MAX} if later
* than {@code Instant.MAX}.
*
* @return an instant representing the same point on the time-line as
* this {@code FileTime} object
* @since 1.8
*/
public Instant toInstant() {
if (instant == null) {
long secs = 0L;
int nanos = 0;
switch (unit) {
case DAYS:
secs = scale(value, SECONDS_PER_DAY,
Long.MAX_VALUE/SECONDS_PER_DAY);
break;
case HOURS:
secs = scale(value, SECONDS_PER_HOUR,
Long.MAX_VALUE/SECONDS_PER_HOUR);
break;
case MINUTES:
secs = scale(value, SECONDS_PER_MINUTE,
Long.MAX_VALUE/SECONDS_PER_MINUTE);
break;
case SECONDS:
secs = value;
break;
case MILLISECONDS:
secs = Math.floorDiv(value, MILLIS_PER_SECOND);
nanos = (int)Math.floorMod(value, MILLIS_PER_SECOND)
* NANOS_PER_MILLI;
break;
case MICROSECONDS:
secs = Math.floorDiv(value, MICROS_PER_SECOND);
nanos = (int)Math.floorMod(value, MICROS_PER_SECOND)
* NANOS_PER_MICRO;
break;
case NANOSECONDS:
secs = Math.floorDiv(value, NANOS_PER_SECOND);
nanos = (int)Math.floorMod(value, NANOS_PER_SECOND);
break;
default : throw new AssertionError("Unit not handled");
}
if (secs <= MIN_SECOND)
instant = Instant.MIN;
else if (secs >= MAX_SECOND)
instant = Instant.MAX;
else
instant = Instant.ofEpochSecond(secs, nanos);
}
return instant;
} }
/** /**
...@@ -176,8 +301,25 @@ public final class FileTime ...@@ -176,8 +301,25 @@ public final class FileTime
*/ */
@Override @Override
public int hashCode() { public int hashCode() {
// hashcode of days/nanos representation to satisfy contract with equals // hashcode of instant representation to satisfy contract with equals
return asDaysAndNanos().hashCode(); return toInstant().hashCode();
}
private long toDays() {
if (unit != null) {
return unit.toDays(value);
} else {
return TimeUnit.SECONDS.toDays(toInstant().getEpochSecond());
}
}
private long toExcessNanos(long days) {
if (unit != null) {
return unit.toNanos(value - unit.convert(days, TimeUnit.DAYS));
} else {
return TimeUnit.SECONDS.toNanos(toInstant().getEpochSecond()
- TimeUnit.DAYS.toSeconds(days));
}
} }
/** /**
...@@ -194,14 +336,52 @@ public final class FileTime ...@@ -194,14 +336,52 @@ public final class FileTime
@Override @Override
public int compareTo(FileTime other) { public int compareTo(FileTime other) {
// same granularity // same granularity
if (unit == other.unit) { if (unit != null && unit == other.unit) {
return (value < other.value) ? -1 : (value == other.value ? 0 : 1); return Long.compare(value, other.value);
} else { } else {
// compare using days/nanos representation when unit differs // compare using instant representation when unit differs
return asDaysAndNanos().compareTo(other.asDaysAndNanos()); long secs = toInstant().getEpochSecond();
long secsOther = other.toInstant().getEpochSecond();
int cmp = Long.compare(secs, secsOther);
if (cmp != 0) {
return cmp;
}
cmp = Long.compare(toInstant().getNano(), other.toInstant().getNano());
if (cmp != 0) {
return cmp;
}
if (secs != MAX_SECOND && secs != MIN_SECOND) {
return 0;
}
// if both this and other's Instant reps are MIN/MAX,
// use daysSinceEpoch and nanosOfDays, which will not
// saturate during calculation.
long days = toDays();
long daysOther = other.toDays();
if (days == daysOther) {
return Long.compare(toExcessNanos(days), other.toExcessNanos(daysOther));
}
return Long.compare(days, daysOther);
} }
} }
// days in a 400 year cycle = 146097
// days in a 10,000 year cycle = 146097 * 25
// seconds per day = 86400
private static final long DAYS_PER_10000_YEARS = 146097L * 25L;
private static final long SECONDS_PER_10000_YEARS = 146097L * 25L * 86400L;
private static final long SECONDS_0000_TO_1970 = ((146097L * 5L) - (30L * 365L + 7L)) * 86400L;
// append year/month/day/hour/minute/second/nano with width and 0 padding
private StringBuilder append(StringBuilder sb, int w, int d) {
while (w > 0) {
sb.append((char)(d/w + '0'));
d = d % w;
w /= 10;
}
return sb;
}
/** /**
* Returns the string representation of this {@code FileTime}. The string * Returns the string representation of this {@code FileTime}. The string
* is returned in the <a * is returned in the <a
...@@ -229,135 +409,67 @@ public final class FileTime ...@@ -229,135 +409,67 @@ public final class FileTime
*/ */
@Override @Override
public String toString() { public String toString() {
String v = valueAsString; if (valueAsString == null) {
if (v == null) { long secs = 0L;
// overflow saturates to Long.MIN_VALUE or Long.MAX_VALUE so this int nanos = 0;
// limits the range: if (instant == null && unit.compareTo(TimeUnit.SECONDS) >= 0) {
// [-292275056-05-16T16:47:04.192Z,292278994-08-17T07:12:55.807Z] secs = unit.toSeconds(value);
long ms = toMillis(); } else {
secs = toInstant().getEpochSecond();
// nothing to do when seconds/minutes/hours/days nanos = toInstant().getNano();
String fractionAsString = "";
if (unit.compareTo(TimeUnit.SECONDS) < 0) {
long fraction = asDaysAndNanos().fractionOfSecondInNanos();
if (fraction != 0L) {
// fraction must be positive
if (fraction < 0L) {
final long MAX_FRACTION_PLUS_1 = 1000L * 1000L * 1000L;
fraction += MAX_FRACTION_PLUS_1;
if (ms != Long.MIN_VALUE) ms--;
}
// convert to String, adding leading zeros as required and
// stripping any trailing zeros
String s = Long.toString(fraction);
int len = s.length();
int width = 9 - len;
StringBuilder sb = new StringBuilder(".");
while (width-- > 0) {
sb.append('0');
}
if (s.charAt(len-1) == '0') {
// drop trailing zeros
len--;
while (s.charAt(len-1) == '0')
len--;
sb.append(s.substring(0, len));
} else {
sb.append(s);
}
fractionAsString = sb.toString();
}
} }
LocalDateTime ldt;
// create calendar to use with formatter. int year = 0;
GregorianCalendar cal = if (secs >= -SECONDS_0000_TO_1970) {
new GregorianCalendar(TimeZone.getTimeZone("UTC"), Locale.ROOT); // current era
if (value < 0L) long zeroSecs = secs - SECONDS_PER_10000_YEARS + SECONDS_0000_TO_1970;
cal.setGregorianChange(new Date(Long.MIN_VALUE)); long hi = Math.floorDiv(zeroSecs, SECONDS_PER_10000_YEARS) + 1;
cal.setTimeInMillis(ms); long lo = Math.floorMod(zeroSecs, SECONDS_PER_10000_YEARS);
ldt = LocalDateTime.ofEpochSecond(lo - SECONDS_0000_TO_1970, nanos, ZoneOffset.UTC);
// years are negative before common era year = ldt.getYear() + (int)hi * 10000;
String sign = (cal.get(Calendar.ERA) == GregorianCalendar.BC) ? "-" : ""; } else {
// before current era
// [-]YYYY-MM-DDThh:mm:ss[.s]Z long zeroSecs = secs + SECONDS_0000_TO_1970;
v = new Formatter(Locale.ROOT) long hi = zeroSecs / SECONDS_PER_10000_YEARS;
.format("%s%tFT%tR:%tS%sZ", sign, cal, cal, cal, fractionAsString) long lo = zeroSecs % SECONDS_PER_10000_YEARS;
.toString(); ldt = LocalDateTime.ofEpochSecond(lo - SECONDS_0000_TO_1970, nanos, ZoneOffset.UTC);
valueAsString = v; year = ldt.getYear() + (int)hi * 10000;
}
return v;
}
/**
* Represents a FileTime's value as two longs: the number of days since
* the epoch, and the excess (in nanoseconds). This is used for comparing
* values with different units of granularity.
*/
private static class DaysAndNanos implements Comparable<DaysAndNanos> {
// constants for conversion
private static final long C0 = 1L;
private static final long C1 = C0 * 24L;
private static final long C2 = C1 * 60L;
private static final long C3 = C2 * 60L;
private static final long C4 = C3 * 1000L;
private static final long C5 = C4 * 1000L;
private static final long C6 = C5 * 1000L;
/**
* The value (in days) since the epoch; can be negative.
*/
private final long days;
/**
* The excess (in nanoseconds); can be negative if days &lt;= 0.
*/
private final long excessNanos;
/**
* Initializes a new instance of this class.
*/
DaysAndNanos(long value, TimeUnit unit) {
long scale;
switch (unit) {
case DAYS : scale = C0; break;
case HOURS : scale = C1; break;
case MINUTES : scale = C2; break;
case SECONDS : scale = C3; break;
case MILLISECONDS : scale = C4; break;
case MICROSECONDS : scale = C5; break;
case NANOSECONDS : scale = C6; break;
default : throw new AssertionError("Unit not handled");
} }
this.days = unit.toDays(value); if (year <= 0) {
this.excessNanos = unit.toNanos(value - (this.days * scale)); year = year - 1;
} }
int fraction = ldt.getNano();
/** StringBuilder sb = new StringBuilder(64);
* Returns the fraction of a second, in nanoseconds. sb.append(year < 0 ? "-" : "");
*/ year = Math.abs(year);
long fractionOfSecondInNanos() { if (year < 10000) {
return excessNanos % (1000L * 1000L * 1000L); append(sb, 1000, Math.abs(year));
} } else {
sb.append(String.valueOf(year));
@Override }
public boolean equals(Object obj) { sb.append('-');
return (obj instanceof DaysAndNanos) ? append(sb, 10, ldt.getMonthValue());
compareTo((DaysAndNanos)obj) == 0 : false; sb.append('-');
} append(sb, 10, ldt.getDayOfMonth());
sb.append('T');
@Override append(sb, 10, ldt.getHour());
public int hashCode() { sb.append(':');
return (int)(days ^ (days >>> 32) ^ append(sb, 10, ldt.getMinute());
excessNanos ^ (excessNanos >>> 32)); sb.append(':');
} append(sb, 10, ldt.getSecond());
if (fraction != 0) {
@Override sb.append('.');
public int compareTo(DaysAndNanos other) { // adding leading zeros and stripping any trailing zeros
if (this.days != other.days) int w = 100_000_000;
return (this.days < other.days) ? -1 : 1; while (fraction % 10 == 0) {
return (this.excessNanos < other.excessNanos) ? -1 : fraction /= 10;
(this.excessNanos == other.excessNanos) ? 0 : 1; w /= 10;
}
append(sb, w, fraction);
}
sb.append('Z');
valueAsString = sb.toString();
} }
return valueAsString;
} }
} }
...@@ -22,14 +22,17 @@ ...@@ -22,14 +22,17 @@
*/ */
/* @test /* @test
* @bug 6844313 * @bug 6844313 8011647
* @summary Unit test for java.nio.file.FileTime * @summary Unit test for java.nio.file.FileTime
*/ */
import java.nio.file.attribute.FileTime; import java.nio.file.attribute.FileTime;
import java.time.Instant;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static java.util.concurrent.TimeUnit.*; import static java.util.concurrent.TimeUnit.*;
import java.util.Random; import java.util.Random;
import java.util.EnumSet;
public class Basic { public class Basic {
...@@ -40,27 +43,59 @@ public class Basic { ...@@ -40,27 +43,59 @@ public class Basic {
long tomorrowInDays = TimeUnit.DAYS.convert(now, MILLISECONDS) + 1; long tomorrowInDays = TimeUnit.DAYS.convert(now, MILLISECONDS) + 1;
long yesterdayInDays = TimeUnit.DAYS.convert(now, MILLISECONDS) - 1; long yesterdayInDays = TimeUnit.DAYS.convert(now, MILLISECONDS) - 1;
Instant nowInstant = Instant.ofEpochMilli(now);
// equals // equals
eq(now, MILLISECONDS, now, MILLISECONDS); eq(now, MILLISECONDS, now, MILLISECONDS);
eq(now, MILLISECONDS, now*1000L, MICROSECONDS); eq(now, MILLISECONDS, now*1000L, MICROSECONDS);
neq(now, MILLISECONDS, 0, MILLISECONDS); neq(now, MILLISECONDS, 0, MILLISECONDS);
neq(now, MILLISECONDS, 0, MICROSECONDS); neq(now, MILLISECONDS, 0, MICROSECONDS);
eq(nowInstant, now, MILLISECONDS);
eq(nowInstant, now*1000L, MICROSECONDS);
neq(nowInstant, 0, MILLISECONDS);
neq(nowInstant, 0, MICROSECONDS);
// compareTo // compareTo
cmp(now, MILLISECONDS, now, MILLISECONDS, 0); cmp(now, MILLISECONDS, now, MILLISECONDS, 0);
cmp(now, MILLISECONDS, now*1000L, MICROSECONDS, 0); cmp(now, MILLISECONDS, now*1000L, MICROSECONDS, 0);
cmp(now, MILLISECONDS, now-1234, MILLISECONDS, 1); cmp(now, MILLISECONDS, now-1234, MILLISECONDS, 1);
cmp(now, MILLISECONDS, now+1234, MILLISECONDS, -1); cmp(now, MILLISECONDS, now+1234, MILLISECONDS, -1);
cmp(tomorrowInDays, DAYS, now, MILLISECONDS, 1); cmp(tomorrowInDays, DAYS, now, MILLISECONDS, 1);
cmp(now, MILLISECONDS, tomorrowInDays, DAYS, -1); cmp(now, MILLISECONDS, tomorrowInDays, DAYS, -1);
cmp(yesterdayInDays, DAYS, now, MILLISECONDS, -1); cmp(yesterdayInDays, DAYS, now, MILLISECONDS, -1);
cmp(now, MILLISECONDS, yesterdayInDays, DAYS, 1); cmp(now, MILLISECONDS, yesterdayInDays, DAYS, 1);
cmp(yesterdayInDays, DAYS, now, MILLISECONDS, -1); cmp(yesterdayInDays, DAYS, now, MILLISECONDS, -1);
cmp(Long.MAX_VALUE, DAYS, Long.MAX_VALUE, NANOSECONDS, 1); cmp(Long.MAX_VALUE, DAYS, Long.MAX_VALUE, NANOSECONDS, 1);
cmp(Long.MAX_VALUE, DAYS, Long.MIN_VALUE, NANOSECONDS, 1); cmp(Long.MAX_VALUE, DAYS, Long.MIN_VALUE, NANOSECONDS, 1);
cmp(Long.MIN_VALUE, DAYS, Long.MIN_VALUE, NANOSECONDS, -1); cmp(Long.MIN_VALUE, DAYS, Long.MIN_VALUE, NANOSECONDS, -1);
cmp(Long.MIN_VALUE, DAYS, Long.MAX_VALUE, NANOSECONDS, -1); cmp(Long.MIN_VALUE, DAYS, Long.MAX_VALUE, NANOSECONDS, -1);
cmp(Instant.MIN, Long.MIN_VALUE, DAYS, 1);
cmp(Instant.MIN, Long.MIN_VALUE, HOURS, 1);
cmp(Instant.MIN, Long.MIN_VALUE, MINUTES, 1);
cmp(Instant.MIN, Long.MIN_VALUE, SECONDS, 1);
cmp(Instant.MIN, Instant.MIN.getEpochSecond() - 1, SECONDS, 1);
cmp(Instant.MIN, Instant.MIN.getEpochSecond() - 100, SECONDS, 1);
cmp(Instant.MIN, Instant.MIN.getEpochSecond(), SECONDS, 0);
cmp(Instant.MAX, Long.MAX_VALUE, DAYS, -1);
cmp(Instant.MAX, Long.MAX_VALUE, HOURS, -1);
cmp(Instant.MAX, Long.MAX_VALUE, MINUTES, -1);
cmp(Instant.MAX, Long.MAX_VALUE, SECONDS, -1);
cmp(Instant.MAX, Instant.MAX.getEpochSecond() + 1, SECONDS, -1);
cmp(Instant.MAX, Instant.MAX.getEpochSecond() + 100, SECONDS, -1);
cmp(Instant.MAX, Instant.MAX.getEpochSecond(), SECONDS, 0);
cmp(nowInstant, now, MILLISECONDS, 0);
cmp(nowInstant, now*1000L, MICROSECONDS, 0);
cmp(nowInstant, now-1234, MILLISECONDS, 1);
cmp(nowInstant, now+1234, MILLISECONDS, -1);
cmp(nowInstant, tomorrowInDays, DAYS, -1);
cmp(nowInstant, yesterdayInDays, DAYS, 1);
// to(TimeUnit) // to(TimeUnit)
to(MILLISECONDS.convert(1, DAYS) - 1, MILLISECONDS); to(MILLISECONDS.convert(1, DAYS) - 1, MILLISECONDS);
to(MILLISECONDS.convert(1, DAYS) + 0, MILLISECONDS); to(MILLISECONDS.convert(1, DAYS) + 0, MILLISECONDS);
...@@ -77,6 +112,64 @@ public class Basic { ...@@ -77,6 +112,64 @@ public class Basic {
to(Long.MAX_VALUE, unit); to(Long.MAX_VALUE, unit);
} }
// toInstant()
int N = 1000;
for (TimeUnit unit : EnumSet.allOf(TimeUnit.class)) {
for (int i = 0; i < N; i++) {
long value = rand.nextLong();
FileTime ft = FileTime.from(value, unit);
Instant instant = ft.toInstant();
if (instant != Instant.MIN && instant != Instant.MAX) {
eqTime(value, unit, instant);
}
}
}
for (TimeUnit unit : EnumSet.allOf(TimeUnit.class)) {
long value = Long.MIN_VALUE;
FileTime ft = FileTime.from(value, unit);
Instant instant = ft.toInstant();
if (unit.compareTo(TimeUnit.SECONDS) < 0) {
eqTime(value, unit, instant);
} else if (!instant.equals(Instant.MIN)) {
throw new RuntimeException("should overflow to MIN");
}
value = Long.MAX_VALUE;
ft = FileTime.from(value, unit);
instant = ft.toInstant();
if (unit.compareTo(TimeUnit.SECONDS) < 0) {
eqTime(value, unit, instant);
} else if (!instant.equals(Instant.MAX)) {
throw new RuntimeException("should overflow to MAX");
}
}
// from(Instant)
final long MAX_SECOND = 31556889864403199L;
for (int i = 0; i < N; i++) {
long v = rand.nextLong();
long secs = v % MAX_SECOND;
Instant instant = Instant.ofEpochSecond(secs, rand.nextInt(1000_000_000));
FileTime ft = FileTime.from(instant);
if (!ft.toInstant().equals(instant) || ft.to(SECONDS) != secs) {
throw new RuntimeException("from(Instant) failed");
}
long millis = v;
instant = Instant.ofEpochMilli(millis);
ft = FileTime.from(instant);
if (!ft.toInstant().equals(instant) ||
ft.toMillis() != instant.toEpochMilli()) {
throw new RuntimeException("from(Instant) failed");
}
long nanos = v;
ft = FileTime.from(nanos, NANOSECONDS);
secs = nanos / 1000_000_000;
nanos = nanos % 1000_000_000;
instant = Instant.ofEpochSecond(secs, nanos);
if (!ft.equals(FileTime.from(instant))) {
throw new RuntimeException("from(Instant) failed");
}
}
// toString // toString
ts(1L, DAYS, "1970-01-02T00:00:00Z"); ts(1L, DAYS, "1970-01-02T00:00:00Z");
ts(1L, HOURS, "1970-01-01T01:00:00Z"); ts(1L, HOURS, "1970-01-01T01:00:00Z");
...@@ -108,11 +201,18 @@ public class Basic { ...@@ -108,11 +201,18 @@ public class Basic {
// NTFS epoch in usec. // NTFS epoch in usec.
ts(-11644473600000000L, MICROSECONDS, "1601-01-01T00:00:00Z"); ts(-11644473600000000L, MICROSECONDS, "1601-01-01T00:00:00Z");
// nulls ts(Instant.MIN, "-1000000001-01-01T00:00:00Z");
ts(Instant.MAX, "1000000000-12-31T23:59:59.999999999Z");
try { try {
FileTime.from(0L, null); FileTime.from(0L, null);
throw new RuntimeException("NullPointerException expected"); throw new RuntimeException("NullPointerException expected");
} catch (NullPointerException npe) { } } catch (NullPointerException npe) { }
try {
FileTime.from(null);
throw new RuntimeException("NullPointerException expected");
} catch (NullPointerException npe) { }
FileTime time = FileTime.fromMillis(now); FileTime time = FileTime.fromMillis(now);
if (time.equals(null)) if (time.equals(null))
throw new RuntimeException("should not be equal to null"); throw new RuntimeException("should not be equal to null");
...@@ -120,6 +220,39 @@ public class Basic { ...@@ -120,6 +220,39 @@ public class Basic {
time.compareTo(null); time.compareTo(null);
throw new RuntimeException("NullPointerException expected"); throw new RuntimeException("NullPointerException expected");
} catch (NullPointerException npe) { } } catch (NullPointerException npe) { }
// Instant + toMilli() overflow
overflow(Long.MAX_VALUE,
FileTime.from(Instant.MAX).toMillis());
overflow(Long.MAX_VALUE,
FileTime.from(Instant.ofEpochSecond(Long.MAX_VALUE / 1000 + 1))
.toMillis());
overflow(Long.MIN_VALUE,
FileTime.from(Instant.MIN).toMillis());
overflow(Long.MIN_VALUE,
FileTime.from(Instant.ofEpochSecond(Long.MIN_VALUE / 1000 - 1))
.toMillis());
// Instant + to(TimeUnit) overflow
overflow(Long.MAX_VALUE,
FileTime.from(Instant.ofEpochSecond(Long.MAX_VALUE / 1000 + 1))
.to(MILLISECONDS));
overflow(Long.MAX_VALUE,
FileTime.from(Instant.ofEpochSecond(Long.MAX_VALUE / 1000,
MILLISECONDS.toNanos(1000)))
.to(MILLISECONDS));
overflow(Long.MIN_VALUE,
FileTime.from(Instant.ofEpochSecond(Long.MIN_VALUE / 1000 - 1))
.to(MILLISECONDS));
overflow(Long.MIN_VALUE,
FileTime.from(Instant.ofEpochSecond(Long.MIN_VALUE / 1000,
-MILLISECONDS.toNanos(1)))
.to(MILLISECONDS));
}
static void overflow(long minmax, long v) {
if (v != minmax)
throw new RuntimeException("saturates to Long.MIN/MAX_VALUE expected");
} }
static void cmp(long v1, TimeUnit u1, long v2, TimeUnit u2, int expected) { static void cmp(long v1, TimeUnit u1, long v2, TimeUnit u2, int expected) {
...@@ -128,6 +261,12 @@ public class Basic { ...@@ -128,6 +261,12 @@ public class Basic {
throw new RuntimeException("unexpected order"); throw new RuntimeException("unexpected order");
} }
static void cmp(Instant ins, long v2, TimeUnit u2, int expected) {
int result = FileTime.from(ins).compareTo(FileTime.from(v2, u2));
if (result != expected)
throw new RuntimeException("unexpected order");
}
static void eq(long v1, TimeUnit u1, long v2, TimeUnit u2) { static void eq(long v1, TimeUnit u1, long v2, TimeUnit u2) {
FileTime t1 = FileTime.from(v1, u1); FileTime t1 = FileTime.from(v1, u1);
FileTime t2 = FileTime.from(v2, u2); FileTime t2 = FileTime.from(v2, u2);
...@@ -137,6 +276,28 @@ public class Basic { ...@@ -137,6 +276,28 @@ public class Basic {
throw new RuntimeException("hashCodes should be equal"); throw new RuntimeException("hashCodes should be equal");
} }
static void eq(Instant ins, long v2, TimeUnit u2) {
FileTime t1 = FileTime.from(ins);
FileTime t2 = FileTime.from(v2, u2);
if (!t1.equals(t2))
throw new RuntimeException("not equal");
if (t1.hashCode() != t2.hashCode())
throw new RuntimeException("hashCodes should be equal");
}
static void eqTime(long value, TimeUnit unit, Instant instant) {
long secs = SECONDS.convert(value, unit);
long nanos = NANOSECONDS.convert(value - unit.convert(secs, SECONDS), unit);
if (nanos < 0) { // normalize nanoOfSecond to positive
secs -= 1;
nanos += 1000_000_000;
}
if (secs != instant.getEpochSecond() || (int)nanos != instant.getNano()) {
System.err.println(" ins=" + instant);
throw new RuntimeException("ft and instant are not the same time point");
}
}
static void neq(long v1, TimeUnit u1, long v2, TimeUnit u2) { static void neq(long v1, TimeUnit u1, long v2, TimeUnit u2) {
FileTime t1 = FileTime.from(v1, u1); FileTime t1 = FileTime.from(v1, u1);
FileTime t2 = FileTime.from(v2, u2); FileTime t2 = FileTime.from(v2, u2);
...@@ -144,6 +305,13 @@ public class Basic { ...@@ -144,6 +305,13 @@ public class Basic {
throw new RuntimeException("should not be equal"); throw new RuntimeException("should not be equal");
} }
static void neq(Instant ins, long v2, TimeUnit u2) {
FileTime t1 = FileTime.from(ins);
FileTime t2 = FileTime.from(v2, u2);
if (t1.equals(t2))
throw new RuntimeException("should not be equal");
}
static void to(long v, TimeUnit unit) { static void to(long v, TimeUnit unit) {
FileTime t = FileTime.from(v, unit); FileTime t = FileTime.from(v, unit);
for (TimeUnit u: TimeUnit.values()) { for (TimeUnit u: TimeUnit.values()) {
...@@ -164,4 +332,14 @@ public class Basic { ...@@ -164,4 +332,14 @@ public class Basic {
throw new RuntimeException(); throw new RuntimeException();
} }
} }
static void ts(Instant instant, String expected) {
String result = FileTime.from(instant).toString();
if (!result.equals(expected)) {
System.err.format("FileTime.from(%s).toString() failed\n", instant);
System.err.format("Expected: %s\n", expected);
System.err.format(" Got: %s\n", result);
throw new RuntimeException();
}
}
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册