我想将任何持续时间/周期字符串文字(采用下面示例中所示的格式)解析为 Java 中的周期/持续时间实例。
我所说的持续时间/期间是指包含基于日期和基于时间的数量的时间量。在 Java 中,单独的
Duration
仅适用于基于时间的数量,如秒、分钟和小时,而单独的 Period
仅适用于基于日期的数量,如年、月和日。
要解析的字符串并不总是包含所有数量。
示例:
29 days 23 hours 59 minutes 20 seconds
4 years 10 months 10 seconds
10 days 9 minutes
1 month
如果可能的话我会怎么做?
我的想法是使用 ThreeTen Extra 项目的
PeriodDuration
类。它结合了您已经提到的 Period
和 Duration
类。一个限制是它的 parse
方法仅接受 ISO 8601 格式,因此我们需要首先将您的字符串转换为该格式。 ISO 8601 格式就像 10 天 9 分钟的 P10DT9M
。 P
是固定的。如果有一个(小时、分钟、秒)将其与基于日期的部分(年、月、周、日)分开,则 T
标记基于时间的部分的开始。一个很好的结果是,我们总是可以判断 M
是否表示月份或分钟:如果 M
在 T
之前或者没有 T
,则表示月份。如果 M
在 T
之后,则表示分钟。我的第一个代码片段将您的字符串转换为 ISO 8601。
String[] examples = {
"29 days 23 hours 59 minutes 20 seconds",
"4 years 10 months 10 seconds",
"10 days 9 minutes",
"1 month"
};
for (String pds : examples) {
// If hours, minutes or seconds are present, put a T before them
String temp = pds.replaceFirst("(\\d+ *(?:hour|minute|second))", " T $1");
// Abbreviate all units to 1 letter; remove spaces
temp = temp.replaceAll("([ymwdhms])[a-z]*", "$1").replace(" ", "");
// Prepend P
String iso = "P" + temp;
System.out.println(iso);
}
到目前为止的输出是:
P29dT23h59m20s P4y10mT10s P10dT9m P1m
在此之后我们应该能够解析:
PeriodDuration pd = PeriodDuration.parse(iso);
我还没有安装 ThreeTen Extra,所以在发布之前我没有尝试过最后一行。 OP 在评论中报道称,它在 ThreeTen Extra 1.6.0 上就像一个魅力。唯一需要注意的是,解析字符串时单位必须按降序排列,否则会抛出
DateTimeParseException
。
不用担心 ISO 8601 格式通常以大写形式给出,而您的单位则以小写形式给出。
parse
方法的文档指出:
这些部分的 ASCII 后缀“Y”代表年份,“M”代表月份, “W”代表周,“D”代表天,“H”代表小时,“M”代表分钟,“S”代表 秒,接受大写或小写。
编辑:感谢 Arvind Kumar Avinash 的评论,如果您也需要接受大写字母,请在正则表达式中添加
(?i)
标志表达式以启用不区分大小写的匹配:
temp = temp.replaceAll("(?i)([ymwdhms])[a-z]*", "$1").replace(" ", "");
ThreeTen Extra 库似乎是一个很好的解决方案,但就我而言,我宁愿不包含一个大型库,只是为了使用一个类的特权,该类基本上是
java.time.Duration
和 java.time.Period
的简单包装器(尽管在 threeten
的情况下,它可能是他们的 Duration
和 Period
版本的包装 - 我遇到过并且大多觉得很烦人)。
所以我写了这个替代品(在这里发布在 CC0 和/或任何 SO 假装他们可以强迫人们许可其受版权保护的内容下):
import java.time.Duration;
import java.time.Instant;
import java.time.Period;
/**
* Copyright Oded Arbel <[email protected]>
* 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();
}
}
我没有审查任何 ThreeTen 代码 - 所以这个实现可能缺少很多功能和/或做错事情 - YMMV。