I am trying to replace org.joda.time.Period with java.time.
We have the following values stored in DB.
P1M, P1Y, P1D, PT1H, PT1M
Just to parse this value,
Period monthly = ISOPeriodFormat.standard().parsePeriod();
This is simple and getting Period as expected. But, now replacing with java.time is troublesome.
Because, P1D, P1M, P1Y should be parsed using the below code,
java.time.Period period = java.time.Period.parse("P1M");
And, P1H and P1D should be parsed using the below code.
Duration dailyD = Duration.parse("P1D");
So, I might need some string check also like,
if(value.startsWith("PT")) {
// Use java.time.Duration
} else {
// Use java.time.Period
}
Is there any better code for this logic?
Also, Finally, I will have to find the number of occurances from some startTime to till date based on the above java.time.Period or java.time.Duration.
Like, If startDateTime is 01/01/2015 08:30,
LocalDateTime startDateTime = // the above startDateTime ..
if(value.startsWith("PT")) {
// Use java.time.Duration
ChronoUnit.SECONDS.between(startDateTime,curentDate)/(duration.toMillis()/1000)
} else {
if(value.endsWith("Y")) {
// Use java.time.Period
ChronoUnit.YEARS.between(startDateTime,curentDate)/(period.getYears()/1000)
} else if(value.endsWith("M")){
ChronoUnit.MONTHS.between(startDateTime,curentDate)/(period.getMonths()/1000)
}
Is there any other better way without this value parsing?
My input could have P2M, P10M, P2Y, PT15M, PT10M. It wont have the combination of both Period and time like P1MT10M. But any number of months, years or days possible.
解决方案
Java-8 does not have such a complex duration type like the class org.joda.time.Period. But you can create your own implementation of the interface TemporalAmount in a straight-forward way:
import java.time.DateTimeException;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAmount;
import java.time.temporal.TemporalUnit;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static java.time.temporal.ChronoUnit.*;
public class SimpleDuration implements TemporalAmount {
private static final List SUPPORTED_UNITS =
Collections.unmodifiableList(Arrays.asList(YEARS, MONTHS, DAYS, HOURS, MINUTES));
private final int amount;
private final ChronoUnit unit;
private SimpleDuration(int amount, ChronoUnit unit) {
this.amount = amount;
this.unit = unit;
}
public static SimpleDuration of(int amount, ChronoUnit unit) {
if (SUPPORTED_UNITS.contains(unit)) {
return new SimpleDuration(amount, unit);
} else {
throw new IllegalArgumentException("Not supported: " + unit);
}
}
@Override
public long get(TemporalUnit unit) {
if (this.unit.equals(unit)) {
return this.amount;
}
return 0;
}
@Override
public List getUnits() {
return SUPPORTED_UNITS;
}
@Override
public Temporal addTo(Temporal temporal) {
return temporal.plus(this.amount, this.unit);
}
@Override
public Temporal subtractFrom(Temporal temporal) {
return temporal.minus(this.amount, this.unit);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SimpleDuration) {
SimpleDuration that = (SimpleDuration) obj;
return this.unit == that.unit && this.amount == that.amount;
} else {
return false;
}
}
@Override
public int hashCode() {
return this.unit.hashCode() + 37 * Integer.hashCode(this.amount);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (this.amount < 0) {
sb.append('-');
}
sb.append('P');
if (this.unit.isTimeBased()) {
sb.append('T');
}
sb.append(Math.abs(this.amount));
switch (this.unit) {
case YEARS :
sb.append('Y');
break;
case MONTHS :
sb.append('M');
break;
case DAYS :
sb.append('D');
break;
case HOURS :
sb.append('H');
break;
case MINUTES :
sb.append('M');
break;
default :
throw new UnsupportedOperationException(this.unit.name());
}
return sb.toString();
}
public static SimpleDuration parse(String input) {
int len = input.length();
int index = 0;
boolean negative = false;
if (len > 0 && input.charAt(0) == '-') {
negative = true; // for XML-Schema (not for ISO-8601)
index++;
}
if (len >= 3 && input.charAt(index) == 'P') {
boolean timeBased = (input.charAt(index + 1) == 'T');
char last = input.charAt(len - 1);
ChronoUnit unit;
switch (last) {
case 'Y' :
unit = YEARS;
break;
case 'M' :
unit = (timeBased ? MINUTES : MONTHS);
break;
case 'D' :
unit = DAYS;
break;
case 'H' :
unit = HOURS;
break;
default :
throw new DateTimeException(
"Unknown unit symbol found at last position: " + input
);
}
if (unit.isTimeBased() != timeBased) {
throw new DateTimeException("Invalid XML-Schema-format: " + input);
}
try {
int amount =
Integer.parseInt(
input.substring(index).substring(timeBased ? 2 : 1, len - 1 - index));
if (amount < 0) {
throw new DateTimeException(
"XML-Schema does not allow negative amounts inside: " + input);
}
return SimpleDuration.of(negative ? -amount : amount, unit);
} catch (NumberFormatException ex) {
throw new DateTimeException("Invalid XML-Schema-format: " + input, ex);
}
}
throw new DateTimeException("Cannot parse: " + input);
}
public static void main(String... args) {
System.out.println(parse("-PT10M")); // -PT10M
}
}