【问题标题】:Parsing duration string into milliseconds将持续时间字符串解析为毫秒
【发布时间】:2012-06-16 19:43:02
【问题描述】:

我需要将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 将它们放在一起并转换为毫秒。

我想我的问题是,这似乎有点矫枉过正,可以更轻松地完成吗?出现了很多关于日期和时间戳的问题,但这可能有点不同。

【问题讨论】:

  • 到目前为止,您的方法看起来不错。不过要小心Integer.parseInt() 和前导零。 ... 08h ... 之类的东西将被解释为八进制并且无法解析。去掉所有前导零。
  • @PhilippReichart 是不是我在上面使用它的方式不安全,因为Integer.parseInt("08") 返回8
  • 也许你对前导空格的想法。 System.out.println("Integer.parseInt("009")); 在我的机器上打印“9”。但是,h 不应传递给 parseInt
  • 我的错。我一直认为Integer.parseInt() 不会接受前导零,但八进制数除外。 javadocs中没有提到它,但似乎工作正常。感谢您的提示:)

标签: java string parsing time


【解决方案1】:

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

Trail: Date Time 了解有关 modern date-time API* 的更多信息。

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 desugaringHow to use ThreeTenABP in Android Project

【讨论】:

    【解决方案2】:

    Java 8 中的新 java.time.Duration 类让您可以开箱即用地解析持续时间:

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

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

    【讨论】:

    • 我正要发布一个新的 SO 问题,用于解析“00:07:21.5958786”并返回一个整数(以毫秒或纳米为单位)。谢谢!顺便说一句,文档移动了,所以值得注意的是您推荐 java.time.Duration,而不是其他 Duration 类。
    【解决方案3】:

    JodaTime library 签出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(...) 部分)可能看起来很棘手,但如果您正在处理 Java 中的句点和持续时间,我真的建议您查看 JodaTime

    另请参阅at this answer about obtaining milliseconds from JodaTime period 以获得更多说明。

    【讨论】:

    • 感谢您非常详细的回复!不幸的是,我将使用不需要添加额外依赖项的解决方案,但我相信有人会使用您的代码。
    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-03-02
      • 1970-01-01
      • 2020-08-27
      • 2013-02-17
      • 2018-10-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多