将持续时间字符串解析为毫秒



我需要将持续时间字符串解析为毫秒的形式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

然后使用时间单位将它们放在一起并转换为毫秒。

我想我的问题是,这似乎有点矫枉过正,能做得更容易吗?出现了很多关于日期和时间戳的问题,但这也许有点不同。

从JodaTime图书馆借出PeriodFormatterPeriodParser

您还可以使用 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(...)部分)可能看起来很棘手,但我真的建议你研究一下JodaTime如果你正在处理Java中的周期和持续时间。

另请查看有关从 JodaTime 时间段获取毫秒的答案,以进一步澄清。

使用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;
}

Java 8 中新的 java.time.Duration 类允许您解析开箱即用的持续时间:

Duration.parse("P98DT01H23M45S").toMillis();

格式略有不同,因此需要在解析之前进行调整。

我建议使用以ISO-8601标准为模型的java.time.Duration,并在Java-8中作为JSR-310实现的一部分引入。

根据 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;
}

要了解有关现代日期时间 API* 的更多信息,请参阅跟踪:日期时间

正则表达式的解释:

  • (?::非捕获组的开始
    • (?::非捕获组的开始
      • (?::非捕获组的开始
        • 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+ API 和如何在 Android Project 中使用 ThreeTenABP。

最新更新