[7.x] Add TimeValue.toHumanReadableString() to allow specifyin… (#43545)

* Enhance TimeValue.toString() to allow specifying fractional values.

This enhances the `TimeValue` class to allow specifying the number of
truncated fractional decimals when calling `toString()`. The default
remains 1, however, more (or less, such as 0) can be specified to change
the output.

This commit also re-organizes some things in `TimeValue` such as putting
all the class variables near the top of the class, and moving the
constructors to the first methods in the class, in order to follow the
structure of our other code.

* Rename `toString(...)` to `toHumanReadableString(...)`
This commit is contained in:
Lee Hinman 2019-06-25 14:06:23 -06:00 committed by GitHub
parent 50eac875e4
commit 8927081981
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 116 additions and 32 deletions

View File

@ -31,6 +31,26 @@ public class TimeValue implements Comparable<TimeValue> {
public static final TimeValue MINUS_ONE = timeValueMillis(-1);
public static final TimeValue ZERO = timeValueMillis(0);
private static final long C0 = 1L;
private static final long C1 = C0 * 1000L;
private static final long C2 = C1 * 1000L;
private static final long C3 = C2 * 1000L;
private static final long C4 = C3 * 60L;
private static final long C5 = C4 * 60L;
private static final long C6 = C5 * 24L;
private final long duration;
private final TimeUnit timeUnit;
public TimeValue(long millis) {
this(millis, TimeUnit.MILLISECONDS);
}
public TimeValue(long duration, TimeUnit timeUnit) {
this.duration = duration;
this.timeUnit = timeUnit;
}
public static TimeValue timeValueNanos(long nanos) {
return new TimeValue(nanos, TimeUnit.NANOSECONDS);
}
@ -51,17 +71,6 @@ public class TimeValue implements Comparable<TimeValue> {
return new TimeValue(hours, TimeUnit.HOURS);
}
private final long duration;
/**
* @return the number of {@link #timeUnit()} units this value contains
*/
public long duration() {
return duration;
}
private final TimeUnit timeUnit;
/**
* @return the unit used for the this time value, see {@link #duration()}
*/
@ -69,13 +78,11 @@ public class TimeValue implements Comparable<TimeValue> {
return timeUnit;
}
public TimeValue(long millis) {
this(millis, TimeUnit.MILLISECONDS);
}
public TimeValue(long duration, TimeUnit timeUnit) {
this.duration = duration;
this.timeUnit = timeUnit;
/**
* @return the number of {@link #timeUnit()} units this value contains
*/
public long duration() {
return duration;
}
public long nanos() {
@ -187,9 +194,31 @@ public class TimeValue implements Comparable<TimeValue> {
*
* Note that this method might produce fractional time values (ex 1.6m) which cannot be
* parsed by method like {@link TimeValue#parse(String, String, String)}.
*
* Also note that the maximum string value that will be generated is
* {@code 106751.9d} due to the way that values are internally converted
* to nanoseconds (106751.9 days is Long.MAX_VALUE nanoseconds)
*/
@Override
public String toString() {
return this.toHumanReadableString(1);
}
/**
* Returns a {@link String} representation of the current {@link TimeValue}.
*
* Note that this method might produce fractional time values (ex 1.6m) which cannot be
* parsed by method like {@link TimeValue#parse(String, String, String)}. The number of
* fractional decimals (up to 10 maximum) are truncated to the number of fraction pieces
* specified.
*
* Also note that the maximum string value that will be generated is
* {@code 106751.9d} due to the way that values are internally converted
* to nanoseconds (106751.9 days is Long.MAX_VALUE nanoseconds)
*
* @param fractionPieces the number of decimal places to include
*/
public String toHumanReadableString(int fractionPieces) {
if (duration < 0) {
return Long.toString(duration);
}
@ -218,25 +247,68 @@ public class TimeValue implements Comparable<TimeValue> {
value = microsFrac();
suffix = "micros";
}
return formatDecimal(value) + suffix;
// Limit fraction pieces to a min of 0 and maximum of 10
return formatDecimal(value, Math.min(10, Math.max(0, fractionPieces))) + suffix;
}
private static String formatDecimal(double value) {
private static String formatDecimal(double value, int fractionPieces) {
String p = String.valueOf(value);
int totalLength = p.length();
int ix = p.indexOf('.') + 1;
int ex = p.indexOf('E');
char fraction = p.charAt(ix);
if (fraction == '0') {
// Location where the fractional values end
int fractionEnd = ex == -1 ? Math.min(ix + fractionPieces, totalLength) : ex;
// Determine the value of the fraction, so if it were .000 the
// actual long value is 0, in which case, it can be elided.
long fractionValue;
try {
fractionValue = Long.parseLong(p.substring(ix, fractionEnd));
} catch (NumberFormatException e) {
fractionValue = 0;
}
if (fractionValue == 0 || fractionPieces <= 0) {
// Either the part of the fraction we were asked to report is
// zero, or the user requested 0 fraction pieces, so return
// only the integral value
if (ex != -1) {
return p.substring(0, ix - 1) + p.substring(ex);
} else {
return p.substring(0, ix - 1);
}
} else {
// Build up an array of fraction characters, without going past
// the end of the string. This keeps track of trailing '0' chars
// that should be truncated from the end to avoid getting a
// string like "1.3000d" (returning "1.3d" instead) when the
// value is 1.30000009
char[] fractions = new char[fractionPieces];
int fracCount = 0;
int truncateCount = 0;
for (int i = 0; i < fractionPieces; i++) {
int position = ix + i;
if (position >= fractionEnd) {
// No more pieces, the fraction has ended
break;
}
char fraction = p.charAt(position);
if (fraction == '0') {
truncateCount++;
} else {
truncateCount = 0;
}
fractions[i] = fraction;
fracCount++;
}
// Generate the fraction string from the char array, truncating any trailing zeros
String fractionStr = new String(fractions, 0, fracCount - truncateCount);
if (ex != -1) {
return p.substring(0, ix) + fraction + p.substring(ex);
return p.substring(0, ix) + fractionStr + p.substring(ex);
} else {
return p.substring(0, ix) + fraction;
return p.substring(0, ix) + fractionStr;
}
}
}
@ -317,14 +389,6 @@ public class TimeValue implements Comparable<TimeValue> {
}
}
private static final long C0 = 1L;
private static final long C1 = C0 * 1000L;
private static final long C2 = C1 * 1000L;
private static final long C3 = C2 * 1000L;
private static final long C4 = C3 * 60L;
private static final long C5 = C4 * 60L;
private static final long C6 = C5 * 24L;
@Override
public boolean equals(Object o) {
if (this == o) return true;

View File

@ -47,6 +47,26 @@ public class TimeValueTests extends ESTestCase {
assertThat("1.5m", equalTo(new TimeValue(90, TimeUnit.SECONDS).toString()));
assertThat("1.5h", equalTo(new TimeValue(90, TimeUnit.MINUTES).toString()));
assertThat("1.5d", equalTo(new TimeValue(36, TimeUnit.HOURS).toString()));
assertThat("1d", equalTo(new TimeValue(36, TimeUnit.HOURS).toHumanReadableString(0)));
assertThat("1d", equalTo(new TimeValue(36, TimeUnit.HOURS).toHumanReadableString(-4)));
assertThat("1.5d", equalTo(new TimeValue(36, TimeUnit.HOURS).toHumanReadableString(2)));
assertThat("1.45d", equalTo(new TimeValue(35, TimeUnit.HOURS).toHumanReadableString(2)));
assertThat("1.4583333333d", equalTo(new TimeValue(35, TimeUnit.HOURS).toHumanReadableString(10)));
assertThat("1d", equalTo(new TimeValue(103723200, TimeUnit.MILLISECONDS).toHumanReadableString(0)));
assertThat("1.2d", equalTo(new TimeValue(103723200, TimeUnit.MILLISECONDS).toHumanReadableString(1)));
assertThat("1.2d", equalTo(new TimeValue(103723200, TimeUnit.MILLISECONDS).toHumanReadableString(2)));
assertThat("1.2d", equalTo(new TimeValue(103723200, TimeUnit.MILLISECONDS).toHumanReadableString(3)));
assertThat("1.2005d", equalTo(new TimeValue(103723200, TimeUnit.MILLISECONDS).toHumanReadableString(4)));
assertThat("1d", equalTo(new TimeValue(86400077, TimeUnit.MILLISECONDS).toHumanReadableString(0)));
assertThat("1d", equalTo(new TimeValue(86400077, TimeUnit.MILLISECONDS).toHumanReadableString(1)));
assertThat("1d", equalTo(new TimeValue(86400077, TimeUnit.MILLISECONDS).toHumanReadableString(2)));
assertThat("1d", equalTo(new TimeValue(86400077, TimeUnit.MILLISECONDS).toHumanReadableString(3)));
assertThat("1d", equalTo(new TimeValue(86400077, TimeUnit.MILLISECONDS).toHumanReadableString(4)));
assertThat("1d", equalTo(new TimeValue(86400077, TimeUnit.MILLISECONDS).toHumanReadableString(5)));
assertThat("1d", equalTo(new TimeValue(86400077, TimeUnit.MILLISECONDS).toHumanReadableString(6)));
assertThat("1.0000008d", equalTo(new TimeValue(86400077, TimeUnit.MILLISECONDS).toHumanReadableString(7)));
assertThat("1.00000089d", equalTo(new TimeValue(86400077, TimeUnit.MILLISECONDS).toHumanReadableString(8)));
assertThat("1.4583333333d", equalTo(new TimeValue(35, TimeUnit.HOURS).toHumanReadableString(Integer.MAX_VALUE)));
assertThat("1000d", equalTo(new TimeValue(1000, TimeUnit.DAYS).toString()));
}