我需要将98d 01h 23m 45s
形式的持续时间字符串解析为毫秒。
我希望这样的持续时间有SimpleDateFormat
,但我找不到任何东西。有人建议赞成或反对尝试使用SDF吗?
我目前的计划是使用正则表达式匹配数字并执行类似
的操作Pattern p = Pattern.compile("(\\d+)");
Matcher m = p.matcher("98d 01h 23m 45s");
if (m.find()) {
int days = Integer.parseInt(m.group());
}
// etc. for hours, minutes, seconds
然后使用TimeUnit将它们放在一起并转换为毫秒。
我想我的问题是,这看起来有点矫枉过正,可以更容易吗?很多关于日期和时间戳的问题都出现了,但这可能有点不同。
答案 0 :(得分:11)
从PeriodFormatter
查看PeriodParser
和JodaTime library。
你也可以使用PeriodFormatterBuilder
为你的字符串构建解析器
String periodString = "98d 01h 23m 45s";
PeriodParser parser = new PeriodFormatterBuilder()
.appendDays().appendSuffix("d ")
.appendHours().appendSuffix("h ")
.appendMinutes().appendSuffix("m ")
.appendSeconds().appendSuffix("s ")
.toParser();
MutablePeriod period = new MutablePeriod();
parser.parseInto(period, periodString, 0, Locale.getDefault());
long millis = period.toDurationFrom(new DateTime(0)).getMillis();
现在,所有这些(特别是toDurationFrom(...)
部分)可能看起来很棘手,但如果您正在处理Java中的句点和持续时间,我建议您查看JodaTime
。
另请查看at this answer about obtaining milliseconds from JodaTime period以获得进一步说明。
答案 1 :(得分:6)
使用Pattern
是一种合理的方式。但为什么不用一个人来获得所有四个领域呢?
Pattern p = Pattern.compile("(\\d+)d\\s+(\\d+)h\\s+(\\d+)m\\s+(\\d+)s");
然后使用索引组提取。
编辑:
建立你的想法,我最终写了以下方法
private static Pattern p = Pattern
.compile("(\\d+)d\\s+(\\d+)h\\s+(\\d+)m\\s+(\\d+)s");
/**
* Parses a duration string of the form "98d 01h 23m 45s" into milliseconds.
*
* @throws ParseException
*/
public static long parseDuration(String duration) throws ParseException {
Matcher m = p.matcher(duration);
long milliseconds = 0;
if (m.find() && m.groupCount() == 4) {
int days = Integer.parseInt(m.group(1));
milliseconds += TimeUnit.MILLISECONDS.convert(days, TimeUnit.DAYS);
int hours = Integer.parseInt(m.group(2));
milliseconds += TimeUnit.MILLISECONDS
.convert(hours, TimeUnit.HOURS);
int minutes = Integer.parseInt(m.group(3));
milliseconds += TimeUnit.MILLISECONDS.convert(minutes,
TimeUnit.MINUTES);
int seconds = Integer.parseInt(m.group(4));
milliseconds += TimeUnit.MILLISECONDS.convert(seconds,
TimeUnit.SECONDS);
} else {
throw new ParseException("Cannot parse duration " + duration, 0);
}
return milliseconds;
}
答案 2 :(得分:4)
Java 8中新的java.time.Duration类让您解析开箱即用的持续时间:
Duration.parse("P98DT01H23M45S").toMillis();
格式略有不同,因此需要在解析之前进行调整。
答案 3 :(得分:2)
我建议使用 java.time.Duration
,它以 ISO-8601 standards 为模型,并作为 JSR-310 implementation 的一部分与 Java-8 一起引入。
根据 ISO-8601 标准,1 天 2 小时的持续时间表示为 P1DT2H,而 2 小时的持续时间表示为 PT2H。将字符串转换为 ISO-8601 格式后,您可以使用 Duration#parse
方法对其进行解析。请注意(感谢 Ole V.V. 提供此信息)表示天、小时、分钟和秒的后缀“D”、“H”、“M”和“S”可以大写或小写。
演示:
import java.time.Duration;
import java.util.regex.Pattern;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
// Test
Stream.of(
"98d 01h 23m 45s",
"01d 02h 03m 04s",
"02h 03m 04s",
"03m 04s",
"04s"
).forEach(s -> System.out.println(durationMillis(s)));
}
static long durationMillis(String s) {
if (Pattern.compile("\\d+d\\s").matcher(s).find()) {
int idxSpace = s.indexOf(" ");
s = "P" + s.substring(0, idxSpace) + "T" + s.substring(idxSpace + 1);
} else
s = "PT" + s;
s = s.replace(" ", "");
return Duration.parse(s).toMillis();
}
}
示例运行:
8472225000
93784000
7384000
184000
4000
另一种解决方法是检索每个部分(天、小时、分钟和秒)的值并将它们添加到 Duration.ZERO
:
static long durationMillis(String s) {
long millis = 0;
Matcher matcher = Pattern.compile("(?:(?:(?:0*(\\d+)d\\s)?0*(\\d+)h\\s)?0*(\\d+)m\\s)?0*(\\d+)s").matcher(s);
if (matcher.find()) {
int days = matcher.group(1) != null ? Integer.parseInt(matcher.group(1)) : 0;
int hours = matcher.group(2) != null ? Integer.parseInt(matcher.group(2)) : 0;
int minutes = matcher.group(3) != null ? Integer.parseInt(matcher.group(3)) : 0;
int seconds = matcher.group(4) != null ? Integer.parseInt(matcher.group(4)) : 0;
millis = Duration.ZERO.plusDays(days).plusHours(hours).plusMinutes(minutes).plusSeconds(seconds).toMillis();
}
return millis;
}
从 modern date-time API 中了解有关 Trail: Date Time* 的更多信息。
对 regex 的解释:
(?:
:非捕获组的开始
(?:
:非捕获组的开始
(?:
:非捕获组的开始
0*
:任意数量的零(\\d+)d\\s
:一位或多位数字(组#1)后跟 d
和一个空格。)?
:非捕获组结束。 ?
使它成为可选的。0*
:任意数量的零(\\d+)h\\s
:一位或多位数字(组#2)后跟 h
和一个空格。)?
:非捕获组结束。 ?
使它成为可选的。0*
:任意数量的零(\\d+)m\\s
:一位或多位数字(组#3)后跟 m
和一个空格。)?
:非捕获组结束。 ?
使它成为可选的。0*
:任意数量的零(\\d+)s
:一位或多位数字(组#4)后跟 s
* 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 & 7. 如果您正在为 Android 项目工作并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaring 和 How to use ThreeTenABP in Android Project。