【问题标题】:How to convert HH:mm:ss.SSS to milliseconds?如何将 HH:mm:ss.SSS 转换为毫秒?
【发布时间】:2012-02-08 05:19:45
【问题描述】:

我有一个字符串00:01:30.500,它相当于90500 毫秒。我尝试使用SimpleDateFormat,它给出包括当前日期在内的毫秒数。我只需要毫秒级的字符串表示。我是否必须编写自定义方法,它将拆分和计算毫秒?或者有没有其他方法可以做到这一点?谢谢。

我试过如下:

        String startAfter = "00:01:30.555";
        SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
        Date date = dateFormat.parse(startAfter);
        System.out.println(date.getTime());

【问题讨论】:

  • 我认为00:01:30.500 表示持续时间,时间量,1 分 30.5 秒。如果是这样,DateSimpleDateFormat 是使用错误的类。持续时间不是日期。另外我建议你永远不要使用这些类。它们设计不佳且过时,尤其是后者出了名的麻烦。使用java.time, the modern Java date and time API

标签: java time timestamp simpledateformat


【解决方案1】:

您可以使用SimpleDateFormat 来执行此操作。你只需要知道两件事。

  1. 所有日期都在内部以 UTC 表示
  2. .getTime() 返回自 1970-01-01 00:00:00 UTC 以来的毫秒数。
package se.wederbrand.milliseconds;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

public class Main {        
    public static void main(String[] args) throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));

        String inputString = "00:01:30.500";

        Date date = sdf.parse("1970-01-01 " + inputString);
        System.out.println("in milliseconds: " + date.getTime());        
    }
}

【讨论】:

  • 无需在前面加上 yyyy-MM-dd 和 1970-01-01。如果时区设置为 GMT,则按照 OP 的方式进行解析。
  • @JBNizet 是真的,而且是一个好点。我特意包含了额外的日期信息,因为我认为它可以更好地显示真实情况。
  • @AndreasWederbrand:让它在这里工作的“诀窍”是将日期设置为 1970 年 1 月 1 日。但是假设我不给这一天,那么它会工作吗?
  • @bks,如果你不设置 1970-01-01,你会在这个问题上得到太多的毫秒数。
【解决方案2】:

如果您想自己解析格式,可以使用正则表达式轻松完成,例如

private static Pattern pattern = Pattern.compile("(\\d{2}):(\\d{2}):(\\d{2}).(\\d{3})");

public static long dateParseRegExp(String period) {
    Matcher matcher = pattern.matcher(period);
    if (matcher.matches()) {
        return Long.parseLong(matcher.group(1)) * 3600000L 
            + Long.parseLong(matcher.group(2)) * 60000 
            + Long.parseLong(matcher.group(3)) * 1000 
            + Long.parseLong(matcher.group(4)); 
    } else {
        throw new IllegalArgumentException("Invalid format " + period);
    }
}

但是,这种解析非常宽松,可以接受 99:99:99.999 并让值溢出。这可能是缺点或功能。

【讨论】:

  • 嘿,我使用了你的方法,但它给出的结果之间添加了 - 字符,这是因为你使用了 3600000 的 L
  • 可能不会,L 是为了让它变长。哪个部分给出了带有 - 的结果? dateParseRegExp 返回一个没有 - 的 long(除非它是负数)。它是具有 - 的匹配组之一吗?
  • 糟糕,不是因为我的坏。
  • "(\\d{2}):(\\d{2}):(\\d{2}).(\\d[0-9])" 如果最后一个数字是 2 位数,这不会失败
  • @Mehran 但它会给出错误的数字答案。你想达到什么目的?如果您需要可变小数位数的解决方案,请作为新问题发布。
【解决方案3】:

使用JODA

PeriodFormatter periodFormat = new PeriodFormatterBuilder()
  .minimumParsedDigits(2)
  .appendHour() // 2 digits minimum
  .appendSeparator(":")
  .minimumParsedDigits(2)
  .appendMinute() // 2 digits minimum
  .appendSeparator(":")
  .minimumParsedDigits(2)
  .appendSecond()
  .appendSeparator(".")
  .appendMillis3Digit()
  .toFormatter();
Period result = Period.parse(string, periodFormat);
return result.toStandardDuration().getMillis();

【讨论】:

    【解决方案4】:

    如果你想使用SimpleDateFormat,你可以写:

    private final SimpleDateFormat sdf =
        new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        { sdf.setTimeZone(TimeZone.getTimeZone("GMT")); }
    
    private long parseTimeToMillis(final String time) throws ParseException
        { return sdf.parse("1970-01-01 " + time).getTime(); }
    

    但是自定义方法会更有效。 SimpleDateFormat,由于它所有的日历支持、时区支持、夏令时支持等等,速度非常慢。如果您确实需要其中一些功能,那么缓慢是值得的,但由于您不需要,因此可能不需要。 (这取决于您调用此方法的频率,以及您的应用程序是否关注效率。)

    另外,SimpleDateFormat 是非线程安全的,这有时很痛苦。 (在不了解您的应用程序的情况下,我无法猜测这是否重要。)

    就个人而言,我可能会编写一个自定义方法。

    【讨论】:

    • 无需在前面加上 yyyy-MM-dd 和 1970-01-01。如果时区设置为 GMT,则按照 OP 的方式进行解析。
    • @JBNizet:在我的系统上确实如此,但 OP 写道,他/她的方法给出了“包括当前日期在内的毫秒数”,所以我假设他/她的 JDK 实现 SimpleDateFormat 一定不能打电话给calendar.clear()。我在 Javadoc 中没有看到任何表明 SimpleDateFormat 必须默认为 1970-01-01 的内容。你知道这是否有保证吗?
    【解决方案5】:

    我提出了两个选择:

    1. Time4J,一个高级外部日期、时间和时间间隔库。
    2. java.time,内置的现代 Java 日期和时间 API。

    SimpleDateFormatDate 是错误的类,因为 1 分钟 30.5 秒的持续时间不是一个日期,而且这些类早已没有任何合理用途。

    时间4J

    这是优雅的解决方案。我们首先声明一个格式化程序:

    private static final Duration.Formatter<ClockUnit> DURATION_FORMAT 
            = Duration.formatter(ClockUnit.class, "hh:mm:ss.fff");
    

    然后像这样解析并转换为毫秒:

        String startAfter = "00:01:30.555";
        
        Duration<ClockUnit> dur = DURATION_FORMAT.parse(startAfter);
        long milliseconds = dur.with(ClockUnit.MILLIS.only())
                .getPartialAmount(ClockUnit.MILLIS);
        
        System.out.format("%d milliseconds%n", milliseconds);
    

    输出是:

    90555 毫秒

    java.time

    java.time.Duration 类只能解析 ISO 8601 格式。所以我首先将您的字符串转换为该格式。它类似于PT00H01M30.555S(不需要前导零,但我为什么要费心删除它们?)

        String startAfter = "00:01:30.555";
        
        String iso = startAfter.replaceFirst(
                "^(\\d{2}):(\\d{2}):(\\d{2}\\.\\d{3})$", "PT$1H$2M$3S");
        Duration dur = Duration.parse(iso);
        long milliseconds = dur.toMillis();
        
        System.out.format("%d milliseconds%n", milliseconds);
    

    输出和之前一样:

    90555 毫秒

    与 Time4J 的另一个区别是,Java Duration 可以直接转换为毫秒,而无需先转换为仅毫秒的Duration

    链接

    【讨论】:

      猜你喜欢
      • 2020-08-25
      • 1970-01-01
      • 1970-01-01
      • 2015-11-14
      • 2011-01-23
      • 1970-01-01
      • 2012-12-14
      • 1970-01-01
      • 2018-05-15
      相关资源
      最近更新 更多