开发者

Better way of printing a mm:ss.ms stop watch string?

开发者 https://www.devze.com 2023-04-11 05:20 出处:网络
I want to show a formatted string with minutes, seconds, and only one character of milliseconds. This is what I put together (mostly from other posts around here):

I want to show a formatted string with minutes, seconds, and only one character of milliseconds. This is what I put together (mostly from other posts around here):

public static final String getTimeDurationAsString(long milliseconds) {
    int millis  = (int) (milliseconds % 1000);
    int seconds = (int) (milliseconds / 1000) % 60;
    int minutes = (int) ((milliseconds / (1000*60)) % 60);
    int hours   = (int) ((milliseconds / (1000*60*60)) % 24);

    StringBuilder sb = new StringBuilder();
    if (hours > 0) {
        sb.append(String.format("%02d", hours));
        sb.append(":");
    }
    sb.append(String.format("%02d", minutes));
    sb.append(":");
    sb.append(String.format("%02d", seconds));
    sb.append(".");
    sb.append(String.format("%03d", millis).substring(0, 1));

    return sb.toString();
}

So I will hide the hours position if the duration didn't exceed 59.9 minutes (which will usually be the case). I did a pretty bad substring() fo开发者_运维百科r the milliseconds position just to grab the first digit. Is there a better way to do the above? I'd like strings like:

00:14.9
00:05.1
00:05.2
33:20:4
etc

I have to generate this string repeatedly for a game I'm making (the above rendered every frame) so afraid it's doing a lot of unnecessary work?

Thanks!


Without a doubt there are more efficient ways to code this up (there are practically always are).

However, on my box your function can format ~130,000 timestamps per second. Clearly, this is plenty fast enough for your stated use case.

That said, if I were coding this up and thought a priori that it's likely to be on the performance-critical path, I'd avoid String.format():

public static final String getTimeDurationAsString(long milliseconds) {
    int millis  = (int) (milliseconds % 1000);
    int seconds = (int) (milliseconds / 1000) % 60;
    int minutes = (int) ((milliseconds / (1000*60)) % 60);
    int hours   = (int) ((milliseconds / (1000*60*60)) % 24);
    StringBuilder sb = new StringBuilder();
    if (hours > 0) {
        sb.append((char)('0' + hours / 10))
          .append((char)('0' + hours % 10)).append(":");
    }
    sb.append((char)('0' + minutes / 10))
      .append((char)('0' + minutes % 10)).append(":")
      .append((char)('0' + seconds / 10))
      .append((char)('0' + seconds % 10)).append(".")
      .append((char)('0' + millis / 100));
    return sb.toString();
}

On my box this can do about 8 million conversions per second. It also creates significantly fewer temporary objects that end up having to be garbage collected.


To convert milliseconds to another unit you can use TimeUnit,

Small presentation what it can:

long minutes = TimeUnit.MILLISECONDS.toMinutes(millis);
long seconds = TimeUnit.MILLISECONDS.toSeconds(millis));

String.format("%02d:%02d", minutes, seconds);

But what you really want to do is to change milliseconds to simple Date:

Date date = new Date(milliseconds);
SimpleDateFormat formatter = new SimpleDateFormat("mm:ss.S");
System.out.println(formatter.format(date));

Output:

getTimeDurationAsString(14009) => 00:14.9
getTimeDurationAsString(2000004) => 33:20.4


You can use String.format to format more than one item into a string at once.

public static final String getTimeDurationAsString(long milliseconds) 
{
    int millis  = (int) (milliseconds % 1000);
    int seconds = (int) (milliseconds / 1000) % 60;
    int minutes = (int) ((milliseconds / (1000*60)) % 60);
    int hours   = (int) ((milliseconds / (1000*60*60)) % 24);

    string hours = "";
    if(hours > 0)
        hours = String.format("%02d:", hours);
    return hours + String.format("%0$:%1$:%2$", minutes, seconds, millis / 100);
}


I think Joda-Time provides the cleanest solution. It might not be the most performant since it is extremely powerful and flexible. But it has proper types to represent durations of time, which is what you are doing, and that gives it a good opportunity to be very performant.

With Joda-Time it's this simple:

private static final PeriodType MSM_PERIOD_TYPE = PeriodType.forFields(new DurationFieldType[]{
        DurationFieldType.minutes(),
        DurationFieldType.seconds(),
        DurationFieldType.millis()});

public static String getTimeDurationAsString(long milliseconds) {
    Period period = new Duration(milliseconds).toPeriod(MSM_PERIOD_TYPE);

    return String.format("%02d:%02d.%01d",
            period.getMinutes(),
            period.getSeconds(),
            period.getMillis() / 100);
}

If you know that the period will never be greater than or equal to an hour then you don't even need MSM_PERIOD_TYPE.

As for the most performant solution...well, I can't really imagine that any of the given solutions would even be a factor when it comes to the rendering code for a game. You could save a bit by avoiding String.format, but this seems to be an ineffective optimization given how much messier it makes the code.

Joda-Time also has a PeriodFormatter class (constructed using PeriodFormatterBuilder) but I didn't see a clean way to include tenths of a second like you desire.

0

精彩评论

暂无评论...
验证码 换一张
取 消