-2
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?

user1798810
  • 61
  • 13
  • 1
    Milliseconds, by definition, cannot be more than three digits. – Joe C Sep 06 '17 at 19:42
  • 2
    `SimpleDateFormat` has milliseconds precision (only 3 digits after decimal point). With more than 3 (like you're trying with `SSSS`), it simply gets lost and returns [strange results](https://stackoverflow.com/a/24617644/7605325). And I think you're also mistaking a time of the day (hour/minute/secs, such as `10 AM` or `5:30 PM`) with a duration (an [amount of time](https://stackoverflow.com/a/44676793/7605325), such as *"2 days, 3 hours and 15 minutes"* or *"10 years and 3 months"*). Although both use the same words ("hours", "minutes", etc), they're not the same thing –  Sep 06 '17 at 19:48
  • 1
    Also, when you multiply `realTimeDbl` by 1000 and cast it to a `long`, the value is rounded to `5234` (you lose the last digit). But this won't work anyway because of `SimpleDateFormat`'s limitation to 3 decimal digits. –  Sep 06 '17 at 20:00
  • anyway we can get 4 decimal digits? – user1798810 Sep 06 '17 at 20:09
  • Not with `SimpleDateFormat`. And after your edit: you're still treating the duration as a date/time, when it's not. There's no built-in formatter for a duration, you'll probably have to do it manually –  Sep 06 '17 at 21:03
  • And once again you are casting to long, losing the last digit... –  Sep 06 '17 at 21:05
  • As Hugo has already said, there is no built-in duration formatter, but maybe you find [following example](https://gist.github.com/MenoData/69ee60a59544f44ce45810e3ed7db86a) for a duration formatter with my lib Time4J interesting (it manages four digits behind the decimal point, too). – Meno Hochschild Sep 07 '17 at 12:19

4 Answers4

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

1

You can use java.time.Duration which is modelled on ISO-8601 standards and was introduced with Java-8 as part of JSR-310 implementation. With Java-9 some more convenience methods were introduced.

Demo:

import java.time.Duration;

public class Main {
    public static void main(String[] args) {
        String realTimeStr = "5.2345";
        Double realTimeDbl = Double.parseDouble(realTimeStr);
        long nanos = (long) (realTimeDbl * 1000_000_000);
        Duration duration = Duration.ofNanos(nanos);
        System.out.println(duration);

        // Custom format
        // ####################################Java-8####################################
        String formattedDuration = String.format("%02d:%02d:%02d.%d", duration.toHours() % 24,
                duration.toMinutes() % 60, duration.toSeconds() % 60, (duration.toNanos() % 1000_000_000) / 100000);
        System.out.println(formattedDuration);
        // ##############################################################################

        // ####################################Java-9####################################
        formattedDuration = String.format("%02d:%02d:%02d.%d", duration.toHoursPart(), duration.toMinutesPart(),
                duration.toSecondsPart(), duration.toNanosPart() / 100000);
        System.out.println(formattedDuration);
        // ##############################################################################

    }
}

Output:

PT5.2345S
00:00:05.2345
00:00:05.2345

Learn about the modern date-time API from Trail: Date Time.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
0

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 =)

Marco Ottina
  • 399
  • 5
  • 12
-1
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

javalearner_heaven
  • 483
  • 1
  • 6
  • 21
  • Why would you put a second pattern character `s` among the milliseconds? – Sotirios Delimanolis Sep 06 '17 at 20:21
  • hah! it knew it was something simple. the part that doesnt make sense is the comments above are claiming that milliseconds in SimpleDateFormat goes up to 3 digits. so how is it that this worked? – user1798810 Sep 06 '17 at 20:27
  • I know SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSSa", Locale.getDefault()); will give Duration: 00:00:05.234AM so tried the small s. I am not sure of this please check it – javalearner_heaven Sep 06 '17 at 20:30
  • 3
    This answer is complete nonsense. @user1798810, the small `s` in the format represent seconds regardless of the fact that it follow the `S`, the milliseconds. You're just copying the `5` from the seconds into your millisecond representation. It doesn't _mean_ what you think it means. – Sotirios Delimanolis Sep 06 '17 at 20:36
  • ok, i will look into it. looks like im getting some odd results in the end. for now i will settle with 3 sig. digits. thx @Sotirios Delimanolis – user1798810 Sep 06 '17 at 20:42
  • 2
    @user1798810 Note also that a `Date` doesn't represent a duration. It's the wrong tool for the job. – Sotirios Delimanolis Sep 06 '17 at 20:43
  • @SotiriosDelimanolis tried using duration and wasnt really successful. But yes, i agree. – user1798810 Sep 06 '17 at 20:47
  • 1
    @javalearner_heaven Just test your code with the input `1.4345` and you'll see that it doesn't work. As already said, appending a `s` to the end just prints again the *seconds* value (which in this case will be `1`). `SimpleDateFormat` simply can't handle more than 3 decimal digits. –  Sep 06 '17 at 21:39
  • 2
    Unbelievable that there are some upvoters for this wrong answer. Never use `SimpleDateFormat` for printing durations, see the good remarks of Hugo. – Meno Hochschild Sep 07 '17 at 11:51