I would like to parse any duration/period string literal (in the format shown in the examples below) into a Period/Duration instance in Java.
By duration/period, I mean an amount of time that contains both date-based and time-based quantities. In Java, Duration
alone only works for time-based quantities like seconds, minutes, and hours, while Period
alone only works for date-based quantities like years, months, and days.
The string to parse won't always contain all quantities.
Examples:
29 days 23 hours 59 minutes 20 seconds
4 years 10 months 10 seconds
10 days 9 minutes
1 month
How would I do that, if possible?
The ThreeTen Extra library seems like a good solution, but in my case I rather not include a large library just for the privilege of using a class that basically is a simple wrapper around java.time.Duration
and java.time.Period
(though in threeten
's case it is probably a wrapper around their versions of Duration
and Period
- which I encountered and mostly find annoying).
So I wrote this replacement (here by posted under CC0 and/or whatever SO pretends they can force people to license their copyrighted content under):
import java.time.Duration;
import java.time.Instant;
import java.time.Period;
/**
* Copyright Oded Arbel <oded@geek.co.il>
* Licensed under [CC0](https://creativecommons.org/public-domain/cc0/)
*/
public class PeriodDuration {
public static final ZoneId ZONE_UTC = ZoneId.ofOffset("", ZoneOffset.UTC);
private Period period;
private Duration duration;
private PeriodDuration(Period period, Duration duration) {
this.period = period;
this.duration = duration;
}
public static PeriodDuration parse(String iso8601period) {
var val = iso8601period.toLowerCase().startsWith("p") ? iso8601period.substring(1) : iso8601period;
var parts = val.split("T",2);
return new PeriodDuration(
parts[0].isEmpty() ? Period.ZERO : Period.parse("P" + parts[0]),
parts.length < 2 || parts[1].isEmpty() ? Duration.ZERO : Duration.parse("PT" + parts[1]));
}
@Override
public String toString() {
return period.toString() + "T" + duration.toString().split("T")[1];
}
public Period getPeriod() {
return period;
}
public Duration getDuration() {
return duration;
}
public Instant addTo(Instant time) {
return time.atZone(ZONE_UTC).plus(period).plus(duration).toInstant();
}
public Instant substractFrom(Instant time) {
return time.atZone(ZONE_UTC).minus(period).minus(duration).toInstant();
}
}
I did not review any ThreeTen code - so this implementation may be missing a lot of features and/or doing things wrong - YMMV.