Format of time is incorrect

2019-09-19 12:42发布

问题:

String realTimeStr = "5.2345";
Double realTimeDbl = Double.parseDouble(realTimeStr);
long   realTimeLng = (long) (realTimeDbl*1000);
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSSS", Locale.getDefault());      
log("Duration: " + sdf.format(new Date(realTimeLng - TimeZone.getDefault().getRawOffset())));

Current output:

Duration: 00:00:05.0234

Desired output:

Duration: 00:00:05.2345

I also tried another method, still no good:

String realTimeStr = "1.4345";
Double realTimeDbl = Double.parseDouble(realTimeStr);               
DateTimeFormatter formatter =               
    DateTimeFormatter.ofPattern("HH:mm:ss.SSSS").withZone(ZoneId.of("UTC"));            
Instant instant = Instant.ofEpochMilli((long)(realTimeDbl*1000));
String t = formatter.format(instant);
System.out.println("Duration: " + t);

Current output:

Duration: 00:00:01.4340

Desired output:

Duration: 00:00:01.4345

I have googled for some time. Any ideas what is causing this?

回答1:

First of all, you're mistaking 2 different concepts:

  1. A time of the day, such as 10 AM or 15:30:45
  2. A duration, which is an amount of time, such as 1 year, 2 months and 10 days or 10 hours, 25 minutes and 30 seconds

Although both might use the same words (such as "hours" and "minutes"), they're not the same thing. A duration is not attached to a chronology (10 hours and 25 minutes relative to what?), it's just the amount of time, by itself.

SimpleDateFormat and DateTimeFormatter are designed to format dates and times of the day, but not durations. Although converting a duration to a time and "pretending" it's a time of the day to format it might work, it's not the right way.

Unfortunately, there are no built-in formatters for a duration. So you'll have to format it manually.

Another detail is that you are multiplying 1.4345 by 1000 (resulting in 1434.5), and then casting to a long(so the value is rounded to 1434) - the last digit is lost.

One way to do it is to parse the string to a double and then multiply by 1 billion to get the value as nanoseconds (I don't know if you'll work with more than 4 digits, so I'm considering nanosecond precision):

// convert the string value to a total of nanoseconds
int nanosPerSecond = 1_000_000_000;
long nanos = (long) (Double.parseDouble(realTimeStr) * nanosPerSecond);

Now we must format it manually. First I've extracted the number of hours, minutes, seconds and nanoseconds from the total nanos value:

long hours = nanos / nanosPerSecond / 3600;
nanos -= hours * 3600 * nanosPerSecond;
long minutes = nanos / nanosPerSecond / 60;
nanos -= minutes * 60 * nanosPerSecond;
long seconds = nanos / nanosPerSecond;
nanos -= seconds * nanosPerSecond;

Then I created an auxiliary method to build the output:

// auxiliary method
public void addValue(long value, StringBuilder sb) {
    if (value < 10) {
        sb.append("0");
    }
    sb.append(value);
}

And used it to join the pieces:

StringBuilder sb = new StringBuilder();
addValue(hours, sb);
sb.append(":");
addValue(minutes, sb);
sb.append(":");
addValue(seconds, sb);
sb.append(".");
addValue(nanos, sb);

// remove the extra zeroes in the end
String output = sb.toString().replaceAll("0*$", "");
System.out.println(output);

The output is:

00:00:01.4345



回答2:

The problem resides in the format, as noticed by Joe C, probably meaning at this line:

SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSSS", Locale.getDefault());

try changing it removing one "S"

SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault());

notify us if it's correct =)



回答3:

package com.stackoverflow.examples;

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

public class TimeStamp {
    public static void main(String[] args) {
        String realTimeStr = "5.2345";
        Double realTimeDbl = Double.parseDouble(realTimeStr);
        long realTimeLng = (long) (realTimeDbl * 1000);
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSSs", Locale.getDefault());
        System.out.println("Duration: " + sdf.format(new Date(realTimeLng - TimeZone.getDefault().getRawOffset())));
    }
}

I tried SSSs instead of SSSS