如何将 HH:mm:ss.SSS 转换为毫秒?

Posted

技术标签:

【中文标题】如何将 HH:mm:ss.SSS 转换为毫秒?【英文标题】:How to convert HH:mm:ss.SSS to milliseconds? 【发布时间】: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。 【参考方案1】:

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

    所有日期都在内部以 UTC 表示 .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("(\\d2):(\\d2):(\\d2).(\\d3)");

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(除非它是负数)。它是具有 - 的匹配组之一吗? 糟糕,不是因为我的坏。 "(\\d2):(\\d2):(\\d2).(\\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】:

我提出了两个选择:

    Time4J,一个高级外部日期、时间和时间间隔库。 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(
            "^(\\d2):(\\d2):(\\d2\\.\\d3)$", "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

链接

Time4J - Advanced Date, Time, Zone and Interval Library for Java Oracle tutorial: Date Time 解释如何使用 java.time。

【讨论】:

以上是关于如何将 HH:mm:ss.SSS 转换为毫秒?的主要内容,如果未能解决你的问题,请参考以下文章

如何在进行选择查询时将 YYYY-MM-DD HH:mm:ss 中的时间戳转换为 Hive 中的 YYYY-MM-DD HH:mm:ss.SSS?

SimpleDateFormat 中的yyyy-MM-dd HH:mm:ss.SSS说明

如何将数据框列传递给scala函数

来自 Hive 的 Pyspark 数据中带有毫秒 'YYYY-MM-DD hh:mm:ss.SSS' 的日期

SQLServer中将yyyy:MM:dd HH:mm:ss.sss转为yyyyMMddHHmmss

SQLServer中将yyyy:MM:dd HH:mm:ss.sss转为yyyyMMddHHmmss