10

I am trying to implement an ETA feature Using System.nanoTime()

startTime = System.nanoTime()
Long elapsedTime = System.nanoTime() - startTime;
Long allTimeForDownloading = (elapsedTime * allBytes / downloadedBytes);

Long remainingTime = allTimeForDownloading - elapsedTime;

But I cannot figure how to get a human readable form of the nanoseconds; for example: 1d 1h, 36s and 3m 50s.

How can I do this?

Sapnesh Naik
  • 11,011
  • 7
  • 63
  • 98

5 Answers5

11

I would say both of these answers are correct, but anyways here is a little shorter version of a function that accepts nano time and returns human-readable String.

private String getReadableTime(Long nanos){

    long tempSec    = nanos/(1000*1000*1000);
    long sec        = tempSec % 60;
    long min        = (tempSec /60) % 60;
    long hour       = (tempSec /(60*60)) % 24;
    long day        = (tempSec / (24*60*60)) % 24;
    
    return String.format("%dd %dh %dm %ds", day,hour,min,sec);

}

For maximum accuracy, instead of integer division, you can use float division and round up.

slashdottir
  • 7,835
  • 7
  • 55
  • 71
Gotiasits
  • 1,135
  • 1
  • 10
  • 21
4

java.time

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) {
        long elapsedTime = 12345678987654321L;

        Duration duration = Duration.ofNanos(elapsedTime);
        // Default format
        System.out.println(duration);

        // Custom format
        // ####################################Java-8####################################
        String formattedElapsedTime = String.format(
                "%02d Day %02d Hour %02d Minute %02d Second %d Millisecond %d Nanosecond", duration.toDays(),
                duration.toHours() % 24, duration.toMinutes() % 60, duration.toSeconds() % 60,
                duration.toMillis() % 1000, duration.toNanos() % 1000000L);
        System.out.println(formattedElapsedTime);
        // ##############################################################################

        // ####################################Java-9####################################
        formattedElapsedTime = String.format("%02d Day %02d Hour %02d Minute %02d Second %d Millisecond %d Nanosecond",
                duration.toDaysPart(), duration.toHoursPart(), duration.toMinutesPart(), duration.toSecondsPart(),
                duration.toMillisPart(), duration.toNanosPart() % 1000000L);
        System.out.println(formattedElapsedTime);
        // ##############################################################################
    }
}

Output:

PT3429H21M18.987654321S
142 Day 21 Hour 21 Minute 18 Second 987 Millisecond 654321 Nanosecond
142 Day 21 Hour 21 Minute 18 Second 987 Millisecond 654321 Nanosecond

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

Community
  • 1
  • 1
Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
  • I really like this solution, but I don't think it handles the ms/ns split correctly. E.g. if I feed it a value of `7,139,410` nanoseconds, it will spit back out `7 Millisecond 7139410 Nanosecond`. I.e. it didn't take the 7ms off of the 7.1 million nanoseconds, effectively reporting that twice. I think you can see it in your example output as well (`987ms 987654321ns`) – DrTeeth Apr 03 '22 at 00:32
  • I used `duration.toNanosPart() % 1000000l` for the nanosecond part for what it's worth. – DrTeeth Apr 03 '22 at 00:43
2

It is my old code, you can convert it to days also.

  private String calculateDifference(long timeInMillis) {
    String hr = "";
    String mn = "";
    long seconds = (int) ((timeInMillis) % 60);
    long minutes = (int) ((timeInMillis / (60)) % 60);
    long hours = (int) ((timeInMillis / (60 * 60)) % 24);

    if (hours < 10) {
        hr = "0" + hours;
    }
    if (minutes < 10) {
        mn = "0" + minutes;
    }
    textView.setText(Html.fromHtml("<i><small;text-align: justify;><font color=\"#000\">" + "Total shift time: " + "</font></small; text-align: justify;></i>" + "<font color=\"#47a842\">" + hr + "h " + mn + "m " + seconds + "s" + "</font>"));
    return hours + ":" + minutes + ":" + seconds;
}
 }
wonderPub
  • 75
  • 4
2

If remainingTime is in nanoseconds, just do the math and append the values to a StringBuilder:

long remainingTime = 5023023402000L;
StringBuilder sb = new StringBuilder();
long seconds = remainingTime / 1000000000;
long days = seconds / (3600 * 24);
append(sb, days, "d");
seconds -= (days * 3600 * 24);
long hours = seconds / 3600;
append(sb, hours, "h");
seconds -= (hours * 3600);
long minutes = seconds / 60;
append(sb, minutes, "m");
seconds -= (minutes * 60);
append(sb, seconds, "s");
long nanos = remainingTime % 1000000000;
append(sb, nanos, "ns");

System.out.println(sb.toString());

// auxiliary method
public void append(StringBuilder sb, long value, String text) {
    if (value > 0) {
        if (sb.length() > 0) {
            sb.append(" ");
        }
        sb.append(value).append(text);
    }
}

The output for the above is:

1h 23m 43s 23402000ns

(1 hour, 23 minutes, 43 seconds and 23402000 nanoseconds).

2

Here's my suggestion. It's based on the fact that the units in TimeUnit.values() are sorted from smallest (nanoseconds) to longest (days).

    private static String getHumanReadableTime(long nanos) {
        TimeUnit unitToPrint = null;
        String result = "";
        long rest = nanos;
        for(TimeUnit t: TimeUnit.values()) {
            if (unitToPrint == null) {
                unitToPrint = t;
                continue;
            }
            // convert 1 of "t" to "unitToPrint", to get the conversion factor
            long factor = unitToPrint.convert(1, t);
            long value = rest % factor;
            rest /= factor;

            result = value + " " + unitToPrint + " " + result;

            unitToPrint = t;
            if (rest == 0) {
                break;
            }
        }
        if (rest != 0) {
            result = rest + " " + unitToPrint + " " + result;
        }

        return result.trim();
    }

Output for getHumanReadableTime(139543185092L) will be

2 MINUTES 19 SECONDS 543 MILLISECONDS 185 MICROSECONDS 92 NANOSECONDS