8148849: Truncating Duration

Introduce a new method  to truncatedTo()

Reviewed-by: rriggs, scolebourne
This commit is contained in:
Nadeesh TV 2016-04-13 10:41:27 +00:00
parent c304110149
commit f1634255f5
2 changed files with 171 additions and 0 deletions

View File

@ -1350,6 +1350,48 @@ public final class Duration
return nanos;
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this {@code Duration} truncated to the specified unit.
* <p>
* Truncating the duration returns a copy of the original with conceptual fields
* smaller than the specified unit set to zero.
* For example, truncating with the {@link ChronoUnit#MINUTES MINUTES} unit will
* round down to the nearest minute, setting the seconds and nanoseconds to zero.
* <p>
* The unit must have a {@linkplain TemporalUnit#getDuration() duration}
* that divides into the length of a standard day without remainder.
* This includes all supplied time units on {@link ChronoUnit} and
* {@link ChronoUnit#DAYS DAYS}. Other ChronoUnits throw an exception.
* <p>
* This instance is immutable and unaffected by this method call.
*
* @param unit the unit to truncate to, not null
* @return a {@code Duration} based on this duration with the time truncated, not null
* @throws DateTimeException if the unit is invalid for truncation
* @throws UnsupportedTemporalTypeException if the unit is not supported
*/
public Duration truncatedTo(TemporalUnit unit) {
Objects.requireNonNull(unit, "unit");
if (unit == ChronoUnit.SECONDS && (seconds >= 0 || nanos == 0)) {
return new Duration(seconds, 0);
} else if (unit == ChronoUnit.NANOS) {
return this;
}
Duration unitDur = unit.getDuration();
if (unitDur.getSeconds() > LocalTime.SECONDS_PER_DAY) {
throw new UnsupportedTemporalTypeException("Unit is too large to be used for truncation");
}
long dur = unitDur.toNanos();
if ((LocalTime.NANOS_PER_DAY % dur) != 0) {
throw new UnsupportedTemporalTypeException("Unit must divide into a standard day without remainder");
}
long nod = (seconds % LocalTime.SECONDS_PER_DAY) * LocalTime.NANOS_PER_SECOND + nanos;
long result = (nod / dur) * dur ;
return plusNanos(result - nod);
}
//-----------------------------------------------------------------------
/**
* Compares this duration to the specified {@code Duration}.

View File

@ -65,9 +65,11 @@ import static java.time.temporal.ChronoUnit.HOURS;
import static java.time.temporal.ChronoUnit.MICROS;
import static java.time.temporal.ChronoUnit.MILLIS;
import static java.time.temporal.ChronoUnit.MINUTES;
import static java.time.temporal.ChronoUnit.MONTHS;
import static java.time.temporal.ChronoUnit.NANOS;
import static java.time.temporal.ChronoUnit.SECONDS;
import static java.time.temporal.ChronoUnit.WEEKS;
import static java.time.temporal.ChronoUnit.YEARS;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
@ -2286,6 +2288,133 @@ public class TCKDuration extends AbstractTCKTest {
test.multipliedBy(Long.MIN_VALUE);
}
//-----------------------------------------------------------------------
// truncated(TemporalUnit)
//-----------------------------------------------------------------------
TemporalUnit NINETY_MINS = new TemporalUnit() {
@Override
public Duration getDuration() {
return Duration.ofMinutes(90);
}
@Override
public boolean isDurationEstimated() {
return false;
}
@Override
public boolean isDateBased() {
return false;
}
@Override
public boolean isTimeBased() {
return true;
}
@Override
public boolean isSupportedBy(Temporal temporal) {
return false;
}
@Override
public <R extends Temporal> R addTo(R temporal, long amount) {
throw new UnsupportedOperationException();
}
@Override
public long between(Temporal temporal1, Temporal temporal2) {
throw new UnsupportedOperationException();
}
@Override
public String toString() {
return "NinetyMins";
}
};
TemporalUnit NINETY_FIVE_MINS = new TemporalUnit() {
@Override
public Duration getDuration() {
return Duration.ofMinutes(95);
}
@Override
public boolean isDurationEstimated() {
return false;
}
@Override
public boolean isDateBased() {
return false;
}
@Override
public boolean isTimeBased() {
return false;
}
@Override
public boolean isSupportedBy(Temporal temporal) {
return false;
}
@Override
public <R extends Temporal> R addTo(R temporal, long amount) {
throw new UnsupportedOperationException();
}
@Override
public long between(Temporal temporal1, Temporal temporal2) {
throw new UnsupportedOperationException();
}
@Override
public String toString() {
return "NinetyFiveMins";
}
};
@DataProvider(name="truncatedToValid")
Object[][] data_truncatedToValid() {
return new Object[][] {
{Duration.ofSeconds(86400 + 3600 + 60 + 1, 123_456_789), NANOS, Duration.ofSeconds(86400 + 3600 + 60 + 1, 123_456_789)},
{Duration.ofSeconds(86400 + 3600 + 60 + 1, 123_456_789), MICROS, Duration.ofSeconds(86400 + 3600 + 60 + 1, 123_456_000)},
{Duration.ofSeconds(86400 + 3600 + 60 + 1, 123_456_789), MILLIS, Duration.ofSeconds(86400 + 3600 + 60 + 1, 1230_00_000)},
{Duration.ofSeconds(86400 + 3600 + 60 + 1, 123_456_789), SECONDS, Duration.ofSeconds(86400 + 3600 + 60 + 1, 0)},
{Duration.ofSeconds(86400 + 3600 + 60 + 1, 123_456_789), MINUTES, Duration.ofSeconds(86400 + 3600 + 60, 0)},
{Duration.ofSeconds(86400 + 3600 + 60 + 1, 123_456_789), HOURS, Duration.ofSeconds(86400 + 3600, 0)},
{Duration.ofSeconds(86400 + 3600 + 60 + 1, 123_456_789), DAYS, Duration.ofSeconds(86400, 0)},
{Duration.ofSeconds(86400 + 3600 + 60 + 1, 123_456_789), NINETY_MINS, Duration.ofSeconds(86400 + 0, 0)},
{Duration.ofSeconds(86400 + 7200 + 60 + 1, 123_456_789), NINETY_MINS, Duration.ofSeconds(86400 + 5400, 0)},
{Duration.ofSeconds(86400 + 10800 + 60 + 1, 123_456_789), NINETY_MINS, Duration.ofSeconds(86400 + 10800, 0)},
{Duration.ofSeconds(-86400 - 3600 - 60 - 1, 123_456_789), MINUTES, Duration.ofSeconds(-86400 - 3600 - 60, 0 )},
{Duration.ofSeconds(-86400 - 3600 - 60 - 1, 123_456_789), MICROS, Duration.ofSeconds(-86400 - 3600 - 60 - 1, 123_457_000)},
{Duration.ofSeconds(86400 + 3600 + 60 + 1, 0), SECONDS, Duration.ofSeconds(86400 + 3600 + 60 + 1, 0)},
{Duration.ofSeconds(-86400 - 3600 - 120, 0), MINUTES, Duration.ofSeconds(-86400 - 3600 - 120, 0)},
{Duration.ofSeconds(-1, 0), SECONDS, Duration.ofSeconds(-1, 0)},
{Duration.ofSeconds(-1, 123_456_789), SECONDS, Duration.ofSeconds(0, 0)},
{Duration.ofSeconds(-1, 123_456_789), NANOS, Duration.ofSeconds(0, -876_543_211)},
{Duration.ofSeconds(0, 123_456_789), SECONDS, Duration.ofSeconds(0, 0)},
{Duration.ofSeconds(0, 123_456_789), NANOS, Duration.ofSeconds(0, 123_456_789)},
};
}
@Test(dataProvider="truncatedToValid")
public void test_truncatedTo_valid(Duration input, TemporalUnit unit, Duration expected) {
assertEquals(input.truncatedTo(unit), expected);
}
@DataProvider(name="truncatedToInvalid")
Object[][] data_truncatedToInvalid() {
return new Object[][] {
{Duration.ofSeconds(1, 123_456_789), NINETY_FIVE_MINS},
{Duration.ofSeconds(1, 123_456_789), WEEKS},
{Duration.ofSeconds(1, 123_456_789), MONTHS},
{Duration.ofSeconds(1, 123_456_789), YEARS},
};
}
@Test(dataProvider="truncatedToInvalid", expectedExceptions=DateTimeException.class)
public void test_truncatedTo_invalid(Duration input, TemporalUnit unit) {
input.truncatedTo(unit);
}
@Test(expectedExceptions=NullPointerException.class)
public void test_truncatedTo_null() {
Duration.ofSeconds(1234).truncatedTo(null);
}
//-----------------------------------------------------------------------
// dividedBy()
//-----------------------------------------------------------------------