如何在Java中格式化hh:mm:ss.SSS格式的经过时间间隔?

时间:2011-07-15 16:19:58

标签: java time simpledateformat

我正在制作一个秒表,我正在使用Java的SimpleDateFormat将毫秒数转换成一个漂亮的“hh:mm:ss:SSS”格式。问题是小时字段总是有一些随机数。这是我正在使用的代码:

public static String formatTime(long millis) {
    SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss.SSS");

    String strDate = sdf.format(millis);
    return strDate;
}

如果我取下hh部分,那么它可以正常工作。否则在hh部分,即使传入的参数(毫秒数)为零,它也会显示随机的内容,如“07”。

虽然我对SimpleDateFormat类知之甚少。谢谢你的帮助。

15 个答案:

答案 0 :(得分:68)

支持您想要做的事情是内置于最新的JDK,其中包含一个名为TimeUnit的鲜为人知的类。

您想要使用java.util.concurrent.TimeUnit来处理间隔

SimpleDateFormat正如它所听到的那样,它会格式化java.util.Date的实例,或者在您的情况下,它会将long值转换为java.util.Date的上下文并且它不知道如何处理间隔,这是您显然正在使用的。

您可以轻松地执行此操作,而无需使用JodaTime等外部库。

import java.util.concurrent.TimeUnit;

public class Main
{        
    private static String formatInterval(final long l)
    {
        final long hr = TimeUnit.MILLISECONDS.toHours(l);
        final long min = TimeUnit.MILLISECONDS.toMinutes(l - TimeUnit.HOURS.toMillis(hr));
        final long sec = TimeUnit.MILLISECONDS.toSeconds(l - TimeUnit.HOURS.toMillis(hr) - TimeUnit.MINUTES.toMillis(min));
        final long ms = TimeUnit.MILLISECONDS.toMillis(l - TimeUnit.HOURS.toMillis(hr) - TimeUnit.MINUTES.toMillis(min) - TimeUnit.SECONDS.toMillis(sec));
        return String.format("%02d:%02d:%02d.%03d", hr, min, sec, ms);
    }

    public static void main(final String[] args)
    {
        System.out.println(formatInterval(Long.parseLong(args[0])));
    }
}

输出将格式化为

13:00:00.000

答案 1 :(得分:28)

更简单的方法是使用DurationFormatUtils中的Apache Commons Lang类:

public static String formatTime(long millis) {
    return DurationFormatUtils.formatDuration(millis, "HH:mm:ss.S");
}

答案 2 :(得分:6)

以下是我使用标准JDK的方法(通过将StringBuilder更改回StringBuffer,这可以用于Java 1.1):

static public String formatMillis(long val) {
    StringBuilder                       buf=new StringBuilder(20);
    String                              sgn="";

    if(val<0) { sgn="-"; val=Math.abs(val); }

    append(buf,sgn,0,(val/3600000)); val%=3600000;
    append(buf,":",2,(val/  60000)); val%=  60000;
    append(buf,":",2,(val/   1000)); val%=   1000;
    append(buf,".",3,(val        ));
    return buf.toString();
    }

/** Append a right-aligned and zero-padded numeric value to a `StringBuilder`. */
static private void append(StringBuilder tgt, String pfx, int dgt, long val) {
    tgt.append(pfx);
    if(dgt>1) {
        int pad=(dgt-1);
        for(long xa=val; xa>9 && pad>0; xa/=10) { pad--;           }
        for(int  xa=0;   xa<pad;        xa++  ) { tgt.append('0'); }
        }
    tgt.append(val);
    }

答案 3 :(得分:6)

为什么不呢?

public static String GetFormattedInterval(final long ms) {
    long millis = ms % 1000;
    long x = ms / 1000;
    long seconds = x % 60;
    x /= 60;
    long minutes = x % 60;
    x /= 60;
    long hours = x % 24;

    return String.format("%02d:%02d:%02d.%03d", hours, minutes, seconds, millis);
}

答案 4 :(得分:5)

这是Joda工作的第一部分,我所做的工作似乎比JDK支持更乏味。请求格式的Joda实现(对零字段做出一些假设)是:

public void printDuration(long milliSecs)
{
    PeriodFormatter formatter = new PeriodFormatterBuilder()
        .printZeroIfSupported()
        .appendHours()
        .appendSeparator(":")
        .minimumPrintedDigits(2)
        .appendMinutes()
        .appendSeparator(":")
        .appendSecondsWithMillis()
        .toFormatter();

    System.out.println(formatter.print(new Period(milliSecs)));
}

答案 5 :(得分:3)

回顾其他答案,我想出了这个功能......

public static String formatInterval(final long interval, boolean millisecs )
{
    final long hr = TimeUnit.MILLISECONDS.toHours(interval);
    final long min = TimeUnit.MILLISECONDS.toMinutes(interval) %60;
    final long sec = TimeUnit.MILLISECONDS.toSeconds(interval) %60;
    final long ms = TimeUnit.MILLISECONDS.toMillis(interval) %1000;
    if( millisecs ) {
        return String.format("%02d:%02d:%02d.%03d", hr, min, sec, ms);
    } else {
        return String.format("%02d:%02d:%02d", hr, min, sec );
    }
}

答案 6 :(得分:2)

这是发生了什么。当您传递毫秒时,该数字相对于1970年1月1日。当您传递0时,它将获取该日期并将其转换为您当地的时区。如果你在中部时间那么恰好是晚上7点。如果你运行它,那么一切都有意义。

new SimpleDateFormat().format(0) => 12/31/69 7:00 PM

编辑,我认为你想要做的是获得经过的时间。为此,我建议使用JodaTime已经很好地完成了这项工作。你做了像

这样的事情
PeriodFormatter formatter = new PeriodFormatterBuilder()
    .appendHours()
    .appendSuffix(" hour", " hours")
    .appendSeparator(" and ")
    .appendMinutes()
    .appendSuffix(" minute", " minutes")
    .appendSeparator(" and ")
    .appendSeconds()
    .appendSuffix(" second", " seconds")
    .toFormatter();

String formattedText = formatter.print(new Period(elapsedMilliSeconds));

答案 7 :(得分:1)

格式根据您当地的时区发生,因此如果您传递0,则假设为0 GMT,然后在您当地的时区进行转换。

答案 8 :(得分:1)

tl; dr

LocalTime                     // Represents a time-of-day, without date and without time zone.
.ofNanoOfDay(                 // Convert a count of nanoseconds into a time-of-day in a generic 24-hour day, ignoring real-world anomalies such as Daylight Saving Time (DST). 
    Duration                  // Class that represents a span-of-time not attached to the timeline.
    .of( milliseconds )       // Parse your count of milliseconds as a span-of-time.
    .toNanos()                // Extract total number of nanoseconds in this span-of-time.
)                             // Returns a `LocalDate` object.
.toString()                   // Generate text in standard ISO 8601 format for a time-of-day (*not* recommended by me). 

java.time.Duration

以小时-分钟-秒为单位表示时间轴上未附加时间跨度的适当类为Duration。对于年月日天,请使用Period

Duration d = Duration.of( milliseconds ) ;

ISO 8601格式

我建议您避免使用HH:MM:SS的时间格式报告时间跨度。我已经看到内在的歧义会导致在现实世界中的业务应用程序中产生误解和混乱。

defined中的ISO 8601PnYnMnDTnHnMnS中有报告这些值的标准。 P表示开始,而T则将任何年月日部分与任何时分秒部分分开。

java.time 类在解析/生成字符串时默认使用ISO 8601标准格式。这包括Duration类。

Duration d = Duration.ofMillis( 5_025_678L ) ;
String output = d.toString() ;

请参阅此code run live at IdeOne.com

  

PT1H23M45.678S

可以解析和生成这些ISO 8601字符串。

Duration d = Duration.parse( "PT1H23M45.678S" ) ;

时间格式

但是,如果您坚持要使用时间格式,可以通过在to…Part对象上调用Duration方法来组合这样的字符串。

Duration d = Duration.ofMillis( 5_025_678L ) ;
String output = d.toHoursPart() + ":" + d.toMinutesPart() + ":" + d.toSecondsPart() + "." + TimeUnit.NANOSECONDS.toMillis( d.toNanosPart() ) ;
  

1:23:45.678

或者我们可以滥用LocalTime类来创建您的字符串。

Duration d = Duration.ofMillis( 5_025_678L ) ;
long nanoOfDay = d.toNanos() ;
LocalTime localTimeBogus = LocalTime.ofNanoOfDay( nanoOfDay ) ;
String output = localTimeBogus.toString() ;

同样,您可以看到此code run live at IdeOne.com

  

01:23:45.678

答案 9 :(得分:0)

使用普通Java Calendar,间隔最多一天(24小时),请参阅我对问题的回答:How to format time intervals in Java?

答案 10 :(得分:0)

变体:最多24小时

经过时间少于24小时的简单格式。超过24小时,代码将仅显示第二天的小时数,并且不会将经过的日期添加到小时。

@addarg(3)
@addarg(3)
@addarg(3)
def func(arg1, arg2, *args):
    return arg1 + arg2 + sum(args)

if __name__ == "__main__":
     print(func(1, 2))

# Output would be 12(1 + 2 + 3 + 3 + 3)

示例代码中缺少功能:

  • 使用“UTC”消除时区
  • 使用24小时格式“HH”

变体:超过24小时

public static String formatElapsedTime(long milliseconds) {

    SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));

    return sdf.format(milliseconds);
}

答案 11 :(得分:0)

这是另一种方法。完全独立且完全向后兼容。无限天数。

private static String slf(double n) {
  return String.valueOf(Double.valueOf(Math.floor(n)).longValue());
}

public static String timeSpan(long timeInMs) {
  double t = Double.valueOf(timeInMs);
  if(t < 1000d)
    return slf(t) + "ms";
  if(t < 60000d)
    return slf(t / 1000d) + "s " +
      slf(t % 1000d) + "ms";
  if(t < 3600000d)
    return slf(t / 60000d) + "m " +
      slf((t % 60000d) / 1000d) + "s " +
      slf(t % 1000d) + "ms";
  if(t < 86400000d)
    return slf(t / 3600000d) + "h " +
      slf((t % 3600000d) / 60000d) + "m " +
      slf((t % 60000d) / 1000d) + "s " +
      slf(t % 1000d) + "ms";
  return slf(t / 86400000d) + "d " +
    slf((t % 86400000d) / 3600000d) + "h " +
    slf((t % 3600000d) / 60000d) + "m " +
    slf((t % 60000d) / 1000d) + "s " +
    slf(t % 1000d) + "ms";
}

答案 12 :(得分:0)

这个实际上有效,但似乎我正在调整方法的意图: - )。

{{1}}

对于那些真的知道如何运作的人,你可能会发现一些警告。

答案 13 :(得分:0)

org.apache.commons.lang.time.DurationFormatUtils.formatDuration(stopWatch.getTime(), "HH:mm:ss")

答案 14 :(得分:0)

您可以在 DateUtils 类中使用 formatElapsedTimeformatSameDayTime 方法。