629

I want to record the time using System.currentTimeMillis() when a user begins something in my program. When he finishes, I will subtract the current System.currentTimeMillis() from the start variable, and I want to show them the time elapsed using a human readable format such as "XX hours, XX mins, XX seconds" or even "XX mins, XX seconds" because its not likely to take someone an hour.

What's the best way to do this?

Ali
  • 261,656
  • 265
  • 575
  • 769

29 Answers29

1326

Use the java.util.concurrent.TimeUnit class:

String.format("%d min, %d sec", 
    TimeUnit.MILLISECONDS.toMinutes(millis),
    TimeUnit.MILLISECONDS.toSeconds(millis) - 
    TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis))
);

Note: TimeUnit is part of the Java 1.5 specification, but toMinutes was added as of Java 1.6.

To add a leading zero for values 0-9, just do:

String.format("%02d min, %02d sec", 
    TimeUnit.MILLISECONDS.toMinutes(millis),
    TimeUnit.MILLISECONDS.toSeconds(millis) - 
    TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis))
);

If TimeUnit or toMinutes are unsupported (such as on Android before API version 9), use the following equations:

int seconds = (int) (milliseconds / 1000) % 60 ;
int minutes = (int) ((milliseconds / (1000*60)) % 60);
int hours   = (int) ((milliseconds / (1000*60*60)) % 24);
//etc...
Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
siddhadev
  • 16,501
  • 2
  • 28
  • 35
  • 5
    int months =(int)((float)days / 30.4368499f); int years = (int)((float)days / 365.242199f); – Nathan Schwermann May 26 '11 at 04:20
  • 7
    Id suggest to use mod 60 instead of subtracting the minutes, it looks cleaner. However, one needs to know that 1 min = 60 sec to do that... =) – Per Alexandersson Feb 12 '12 at 21:15
  • Oh no ... why is this solution so much voted up? I would suggest not to use such long, explicit code. There are **much better solutions** (based on a single time object like **`new Date(ms)`**, or similar using other libs) **posted below** considering **`SimpleDateFormat("mm 'min,' ss 'sec'")`** (Java native), **DurationFormatUtils** (famous commons-lang lib dependency), **`YodaTime`** (famous Yoda lib dependency) and **`String.format("%tT")`** (Java native) (I'll vote this one down and others up because the mentioned solutions should be prefered in general for best practice) – Andreas Covidiot Sep 26 '14 at 08:53
  • 2
    @AndreasDietrich SimpleDateFormat doesn't work for time differences as it only return date from epoch! – Muhammad Babar Oct 23 '14 at 09:30
  • 9
    `int hours = (int) ((milliseconds / (1000*60*60)) % 24)` this doesn't work! instead it should be like this `int hours = (int) (milliseconds / (1000*60*60)) ;` – Muhammad Babar Oct 23 '14 at 09:35
  • Also could you please explain why are you using % operator? – Muhammad Babar Oct 23 '14 at 09:36
  • @MuhammadBabar: **`SimpleDateFormat`** I mentioned for the simple output formatting feature (a little **independent of the difference calculation**). If one wants to use it for this, of course one has to give it a `Date` object that is already a calculated difference added to `new Date(0)` with some additional ugly magic related to the underlying timezone stuff. The calculation is not pretty and I don't wanna mention it here, but my answer was intended to point to classes that help to encapsulate functionality using existing libraries/classes. – Andreas Covidiot Oct 23 '14 at 13:16
  • 1
    @MuhammadBabar: The **`%` operator (modulo) is needed to "throw away" everything bigger than the current "sub-time scope" (e.g. *91 s should become "31 sec" displayed as "1 min + 31 sec"*)** – Andreas Covidiot Oct 23 '14 at 13:16
  • 1
    as MuhammadBabar mentioned **the biggest time/date part in the calculation above should not use the `%` (modulo) operator** since it should not be divided by some factor even if it looks inconvinient if an *hour* may look bigger than expected, **e.g. 30 days displayed as "hh:mm:ss" would look like "720:00:00" (correct) and not "00:00:00" (incorrect) (if moduloed by `%`**) .. all these problems because its complicated and using libraries prevents such hassle! – Andreas Covidiot Oct 23 '14 at 13:23
  • 7
    If somebody wants a format for video files (**h:mm:ss**): `String.format("%01d:%02d:%02d", hours, minutes, seconds);` – kazy Nov 26 '14 at 16:09
  • what about leap seconds? That my break some to the suggested conversions. – floquet22 Jun 01 '16 at 07:08
  • 2
    There is a problem. When milliseconds is 59999, actually it is 1 minute but it will be computed as 59 seconds and 999 milliseconds is lost. – Hexise Sep 23 '18 at 13:54
136

Based on @siddhadev's answer, I wrote a function which converts milliseconds to a formatted string:

   /**
     * Convert a millisecond duration to a string format
     * 
     * @param millis A duration to convert to a string form
     * @return A string of the form "X Days Y Hours Z Minutes A Seconds".
     */
    public static String getDurationBreakdown(long millis) {
        if(millis < 0) {
            throw new IllegalArgumentException("Duration must be greater than zero!");
        }

        long days = TimeUnit.MILLISECONDS.toDays(millis);
        millis -= TimeUnit.DAYS.toMillis(days);
        long hours = TimeUnit.MILLISECONDS.toHours(millis);
        millis -= TimeUnit.HOURS.toMillis(hours);
        long minutes = TimeUnit.MILLISECONDS.toMinutes(millis);
        millis -= TimeUnit.MINUTES.toMillis(minutes);
        long seconds = TimeUnit.MILLISECONDS.toSeconds(millis);

        StringBuilder sb = new StringBuilder(64);
        sb.append(days);
        sb.append(" Days ");
        sb.append(hours);
        sb.append(" Hours ");
        sb.append(minutes);
        sb.append(" Minutes ");
        sb.append(seconds);
        sb.append(" Seconds");

        return(sb.toString());
    }
Brent Writes Code
  • 19,075
  • 7
  • 52
  • 56
  • 2
    You should do a checking there for single/multiple, in order to avoid this kind of situation : `1 Days 1 Hours 1 Minutes 1 Seconds` – Daniel Jul 02 '14 at 07:21
  • 11
    We could use modulus function instead of subtraction: `long days = TimeUnit.MILLISECONDS.toDays(millis);` `long hours = TimeUnit.MILLISECONDS.toHours(millis) % 24;` `long minutes = TimeUnit.MILLISECONDS.toMinutes(millis) % 60;` `long seconds = TimeUnit.MILLISECONDS.toSeconds(millis) % 60;` `long milliseconds = millis % 1000;` and also String.format method: `return String.format("%d Days %d Hours %d Minutes %d Seconds %d Milliseconds", days, hours, minutes, seconds, milliseconds);` – Jose Tepedino Aug 13 '16 at 09:34
92
long time = 1536259;

return (new SimpleDateFormat("mm:ss:SSS")).format(new Date(time));

Prints:

25:36:259

Arshad Ali
  • 3,082
  • 12
  • 56
  • 99
Damien
  • 1,401
  • 9
  • 4
  • 1
    I like the above approach the best if for nothing else its simplicity! Since we are dealing with times and durations I typically use Joda. An example if you have two DateTimes, start and end respectively: `Duration dur = new Duration(start, end);` `long millis = dur.getMillis();` – TechTrip Apr 02 '12 at 14:42
  • 1
    I should have noted 2 ways to use Joda formatters. First variation on your theme: `DateTimeFormat.forPattern("mm:ss:SSS").print(new DateTime(time));` Or convert the Duration to a Period that can automatically be printed using a Joda `PeriodFormatter`. The conversion could have a loss of precision if other than ISO chronology. Suppose a Duration represented by the variable `duration`. `Period period = duration.toPeriod().normalizedStandard(PeriodType.time());` `PeriodFormat.getDefault().print(period))` Output is awesome: 1 second and 581 milliseconds, answering the main question above. – TechTrip Apr 02 '12 at 16:03
  • 3
    A bit late here :) But wouldn't you need to put simpleDateFormat.setTimezone(TimeZone.getTimeZone("GMT")) here unless that is your actual timezone? – Olle Söderström Apr 19 '13 at 12:14
  • 1
    @OlleSöderström There is indeed an issue with timezone here. But setting it to GMT or UTC causes the hour part to be 12 instead of 0 for all durations shorter than 1 hour. – Episodex Jul 10 '15 at 13:27
  • 3
    Timezone isn't the only issue here... this answer is misleading and could seriously confuse Java newbies. Even ignoring the redundant outer brackets, the OP asked how to present a _duration_ (a difference between two points in time, measured in ms). Calling that duration "time" and treating it as an offset since 1 Jan, 1970 just for the sake of formatting its smaller components is just... wrong imo. What's more, the very same approach had already been suggested 3 years earlier by [this answer](http://stackoverflow.com/a/625444/983430) (see comments there for more details on timezone problems). – Amos M. Carpenter Apr 04 '16 at 06:21
  • It builds 2 objects, one of which is very heavy (Date), depending on the amount of data to be handled, this could be slow – JBoy May 19 '16 at 13:57
38

Using the java.time package in Java 8:

Instant start = Instant.now();
Thread.sleep(63553);
Instant end = Instant.now();
System.out.println(Duration.between(start, end));

Output is in ISO 8601 Duration format: PT1M3.553S (1 minute and 3.553 seconds).

Vitalii Fedorenko
  • 110,878
  • 29
  • 149
  • 111
  • 2
    See also [How to format a duration in java? (e.g format H:MM:SS)](https://stackoverflow.com/questions/266825/how-to-format-a-duration-in-java-e-g-format-hmmss) – Vadzim Mar 03 '18 at 16:44
  • 3
    (ANDROID) Requires API level 26 – Someone Somewhere Jun 11 '18 at 19:37
  • 2
    @SomeoneSomewhere For Android under API level 26 use the backport, see [How to use ThreeTenABP in Android Project](https://stackoverflow.com/questions/38922754/how-to-use-threetenabp-in-android-project). – Ole V.V. Oct 05 '19 at 16:59
36

Uhm... how many milliseconds are in a second? And in a minute? Division is not that hard.

int seconds = (int) ((milliseconds / 1000) % 60);
int minutes = (int) ((milliseconds / 1000) / 60);

Continue like that for hours, days, weeks, months, year, decades, whatever.

Bombe
  • 81,643
  • 20
  • 123
  • 127
  • 3
    Actually, doing this for anything longer than an hour is not a good idea since the results could be wrong/unintuitive when daylight savings time (days of 23 or 24 hours) or leap years are involved. If I read "X will happen in 1 year/month", I'd expect it to be the same date and time. – Michael Borgwardt Mar 09 '09 at 10:54
  • 6
    System.currentTimeMillis() is immune against DST so this will not be confused by additional or missing hours. If you need to show the difference between two specific dates, you’re better off constructing Date objects with the given time and show the difference between those two. – Bombe Mar 09 '09 at 11:48
  • 1
    Beyond weeks, it is undefined, since month length is variable. So indeed, you need to compute relative to a given time reference. – PhiLho Mar 10 '09 at 15:46
30

I would not pull in the extra dependency just for that (division is not that hard, after all), but if you are using Commons Lang anyway, there are the DurationFormatUtils.

Example Usage (adapted from here):

import org.apache.commons.lang3.time.DurationFormatUtils

public String getAge(long value) {
    long currentTime = System.currentTimeMillis();
    long age = currentTime - value;
    String ageString = DurationFormatUtils.formatDuration(age, "d") + "d";
    if ("0d".equals(ageString)) {
        ageString = DurationFormatUtils.formatDuration(age, "H") + "h";
        if ("0h".equals(ageString)) {
            ageString = DurationFormatUtils.formatDuration(age, "m") + "m";
            if ("0m".equals(ageString)) {
                ageString = DurationFormatUtils.formatDuration(age, "s") + "s";
                if ("0s".equals(ageString)) {
                    ageString = age + "ms";
                }
            }
        }
    }
    return ageString;
}   

Example:

long lastTime = System.currentTimeMillis() - 2000;
System.out.println("Elapsed time: " + getAge(lastTime)); 

//Output: 2s

Note: To get millis from two LocalDateTime objects you can use:

long age = ChronoUnit.MILLIS.between(initTime, LocalDateTime.now())
lepe
  • 24,677
  • 9
  • 99
  • 108
Thilo
  • 257,207
  • 101
  • 511
  • 656
  • 1
    Great, I was just asking myself if there was a robust library that included this sort of things. – Lajcik Jan 18 '11 at 11:50
  • As a link is generally not very useful, I added an example on how can it be used. – lepe Aug 27 '19 at 01:07
28

Either hand divisions, or use the SimpleDateFormat API.

long start = System.currentTimeMillis();
// do your work...
long elapsed = System.currentTimeMillis() - start;
DateFormat df = new SimpleDateFormat("HH 'hours', mm 'mins,' ss 'seconds'");
df.setTimeZone(TimeZone.getTimeZone("GMT+0"));
System.out.println(df.format(new Date(elapsed)));

Edit by Bombe: It has been shown in the comments that this approach only works for smaller durations (i.e. less than a day).

Community
  • 1
  • 1
cadrian
  • 7,332
  • 2
  • 33
  • 42
  • Wow. That is an evil, timezone-dependent hack. It will break mercilessly when you have a timezone offset that is not a multiple of 60 minutes (and we have a couple of those pesky 30-minute offset timezones in the world). – Bombe Mar 09 '09 at 08:51
  • Also, it will break just as bad as soon as you include the hours in the format string and are not at GMT+0, for the same reasons. – Bombe Mar 09 '09 at 08:52
  • We do? Really? Where? Not doubting you, just never heard of it before - some new pitfall to consider ;-) – Treb Mar 09 '09 at 08:53
  • Yes. Check “List of time zones” on Wikipedia, e.g. Nepal is at GMT+05:45. – Bombe Mar 09 '09 at 09:08
  • Yeah, and Hyderabad is GMT+5.5 :\ – GaZ Mar 09 '09 at 10:05
  • 2
    cadrian, now it will only work for durations less than a day. The number of hours will always be between 0 and 23, inclusive. – Bombe Mar 09 '09 at 11:46
24

Just to add more info if you want to format like: HH:mm:ss

0 <= HH <= infinite

0 <= mm < 60

0 <= ss < 60

use this:

int h = (int) ((startTimeInMillis / 1000) / 3600);
int m = (int) (((startTimeInMillis / 1000) / 60) % 60);
int s = (int) ((startTimeInMillis / 1000) % 60);

I just had this issue now and figured this out

fredcrs
  • 3,558
  • 7
  • 33
  • 55
  • 1
    Best answer here. Why does everyone want to make things so complicated? This is simple math, folks. – Lambart Jun 29 '16 at 18:27
14

Shortest solution:

Here's probably the shortest which also deals with time zones.

System.out.printf("%tT", millis-TimeZone.getDefault().getRawOffset());

Which outputs for example:

00:18:32

Explanation:

%tT is the time formatted for the 24-hour clock as %tH:%tM:%tS.

%tT also accepts longs as input, so no need to create a Date. printf() will simply print the time specified in milliseconds, but in the current time zone therefore we have to subtract the raw offset of the current time zone so that 0 milliseconds will be 0 hours and not the time offset value of the current time zone.

Note #1: If you need the result as a String, you can get it like this:

String t = String.format("%tT", millis-TimeZone.getDefault().getRawOffset());

Note #2: This only gives correct result if millis is less than a day because the day part is not included in the output.

icza
  • 389,944
  • 63
  • 907
  • 827
  • This is such a great short cut, but when I use it I don't get the expected results. So I have: `long millis = 8398;` and when I pass that to `String.format("%tT", millis)` my output is 19:00:08. Where does the 19 come from? – Nelda.techspiress Aug 15 '17 at 15:15
  • 1
    @Nelda.techspiress That's the offset of your timezone. You have to subtract `TimeZone.getDefault().getRawOffset()` from it, exactly as written in the answer: `String.format("%tT", millis-TimeZone.getDefault().getRawOffset())` – icza Aug 15 '17 at 15:30
  • that took care of it. Thank you for clarifying. – Nelda.techspiress Aug 15 '17 at 16:16
13

Revisiting @brent-nash contribution, we could use modulus function instead of subtractions and use String.format method for the result string:

  /**
   * Convert a millisecond duration to a string format
   * 
   * @param millis A duration to convert to a string form
   * @return A string of the form "X Days Y Hours Z Minutes A Seconds B Milliseconds".
   */
   public static String getDurationBreakdown(long millis) {
       if (millis < 0) {
          throw new IllegalArgumentException("Duration must be greater than zero!");
       }

       long days = TimeUnit.MILLISECONDS.toDays(millis);
       long hours = TimeUnit.MILLISECONDS.toHours(millis) % 24;
       long minutes = TimeUnit.MILLISECONDS.toMinutes(millis) % 60;
       long seconds = TimeUnit.MILLISECONDS.toSeconds(millis) % 60;
       long milliseconds = millis % 1000;

       return String.format("%d Days %d Hours %d Minutes %d Seconds %d Milliseconds",
                            days, hours, minutes, seconds, milliseconds);
   }
Johnny Five
  • 987
  • 1
  • 14
  • 29
Jose Tepedino
  • 1,524
  • 15
  • 18
11

I think the best way is:

String.format("%d min, %d sec", 
    TimeUnit.MILLISECONDS.toSeconds(length)/60,
    TimeUnit.MILLISECONDS.toSeconds(length) % 60 );
Mr.Wizard
  • 24,179
  • 5
  • 44
  • 125
Xtera
  • 119
  • 1
  • 2
9

Joda-Time

Using Joda-Time:

DateTime startTime = new DateTime();

// do something

DateTime endTime = new DateTime();
Duration duration = new Duration(startTime, endTime);
Period period = duration.toPeriod().normalizedStandard(PeriodType.time());
System.out.println(PeriodFormat.getDefault().print(period));
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
dogbane
  • 266,786
  • 75
  • 396
  • 414
  • 1
    No need for that `PeriodFormat` in the last line. Simply call `Period::toString` to get an [ISO 8601 Duration](https://en.wikipedia.org/wiki/ISO_8601#Durations) string by default. – Basil Bourque Mar 13 '16 at 23:35
8

Firstly, System.currentTimeMillis() and Instant.now() are not ideal for timing. They both report the wall-clock time, which the computer doesn't know precisely, and which can move erratically, including going backwards if for example the NTP daemon corrects the system time. If your timing happens on a single machine then you should instead use System.nanoTime().

Secondly, from Java 8 onwards java.time.Duration is the best way to represent a duration:

long start = System.nanoTime();
// do things...
long end = System.nanoTime();
Duration duration = Duration.ofNanos(end - start);
System.out.println(duration); // Prints "PT18M19.511627776S"
System.out.printf("%d Hours %d Minutes %d Seconds%n",
        duration.toHours(), duration.toMinutes() % 60, duration.getSeconds() % 60);
// prints "0 Hours 18 Minutes 19 Seconds"
MikeFHay
  • 8,562
  • 4
  • 31
  • 52
8

For those who looking for Kotlin code:

fun converter(millis: Long): String =
        String.format(
            "%02d : %02d : %02d",
            TimeUnit.MILLISECONDS.toHours(millis),
            TimeUnit.MILLISECONDS.toMinutes(millis) - TimeUnit.HOURS.toMinutes(
                TimeUnit.MILLISECONDS.toHours(millis)
            ),
            TimeUnit.MILLISECONDS.toSeconds(millis) - TimeUnit.MINUTES.toSeconds(
                TimeUnit.MILLISECONDS.toMinutes(millis)
            )
        )

Sample output: 09 : 10 : 26

Kaaveh Mohamedi
  • 1,399
  • 1
  • 15
  • 36
6

My simple calculation:

String millisecToTime(int millisec) {
    int sec = millisec/1000;
    int second = sec % 60;
    int minute = sec / 60;
    if (minute >= 60) {
        int hour = minute / 60;
        minute %= 60;
        return hour + ":" + (minute < 10 ? "0" + minute : minute) + ":" + (second < 10 ? "0" + second : second);
    }
    return minute + ":" + (second < 10 ? "0" + second : second);
}

Happy coding :)

Shohan Ahmed Sijan
  • 4,391
  • 1
  • 33
  • 39
  • I suppose long is more appropriate for this function as System.currentTimeMillis() returns a long value. – aprodan Jun 28 '18 at 03:08
5

for Android below API 9

(String.format("%d hr %d min, %d sec", millis/(1000*60*60), (millis%(1000*60*60))/(1000*60), ((millis%(1000*60*60))%(1000*60))/1000)) 
Pradeep
  • 2,530
  • 26
  • 37
4
    long startTime = System.currentTimeMillis();
    // do your work...
    long endTime=System.currentTimeMillis();
    long diff=endTime-startTime;       
    long hours=TimeUnit.MILLISECONDS.toHours(diff);
    diff=diff-(hours*60*60*1000);
    long min=TimeUnit.MILLISECONDS.toMinutes(diff);
    diff=diff-(min*60*1000);
    long seconds=TimeUnit.MILLISECONDS.toSeconds(diff);
    //hour, min and seconds variables contains the time elapsed on your work
Fathah Rehman P
  • 8,401
  • 4
  • 40
  • 42
  • 3
    The formula on line 6 is wrong. `diff=diff-(hours*60*1000);` should be `diff=diff-(hours*60*60*1000);`. I tried editing it but StackOverflow's annoying edit policy says it's not enough characters for an edit. – quux00 Jul 23 '13 at 19:08
4

Here is an answer based on Brent Nash answer, Hope that helps !

public static String getDurationBreakdown(long millis)
{
    String[] units = {" Days ", " Hours ", " Minutes ", " Seconds "};
    Long[] values = new Long[units.length];
    if(millis < 0)
    {
        throw new IllegalArgumentException("Duration must be greater than zero!");
    }

    values[0] = TimeUnit.MILLISECONDS.toDays(millis);
    millis -= TimeUnit.DAYS.toMillis(values[0]);
    values[1] = TimeUnit.MILLISECONDS.toHours(millis);
    millis -= TimeUnit.HOURS.toMillis(values[1]);
    values[2] = TimeUnit.MILLISECONDS.toMinutes(millis);
    millis -= TimeUnit.MINUTES.toMillis(values[2]);
    values[3] = TimeUnit.MILLISECONDS.toSeconds(millis);

    StringBuilder sb = new StringBuilder(64);
    boolean startPrinting = false;
    for(int i = 0; i < units.length; i++){
        if( !startPrinting && values[i] != 0)
            startPrinting = true;
        if(startPrinting){
            sb.append(values[i]);
            sb.append(units[i]);
        }
    }

    return(sb.toString());
}
4

This is easier in Java 9:

    Duration elapsedTime = Duration.ofMillis(millisDiff );
    String humanReadableElapsedTime = String.format(
            "%d hours, %d mins, %d seconds",
            elapsedTime.toHours(),
            elapsedTime.toMinutesPart(),
            elapsedTime.toSecondsPart());

This produces a string like 0 hours, 39 mins, 9 seconds.

If you want to round to whole seconds before formatting:

    elapsedTime = elapsedTime.plusMillis(500).truncatedTo(ChronoUnit.SECONDS);

To leave out the hours if they are 0:

    long hours = elapsedTime.toHours();
    String humanReadableElapsedTime;
    if (hours == 0) {
        humanReadableElapsedTime = String.format(
                "%d mins, %d seconds",
                elapsedTime.toMinutesPart(),
                elapsedTime.toSecondsPart());

    } else {
        humanReadableElapsedTime = String.format(
                "%d hours, %d mins, %d seconds",
                hours,
                elapsedTime.toMinutesPart(),
                elapsedTime.toSecondsPart());
    }

Now we can have for example 39 mins, 9 seconds.

To print minutes and seconds with leading zero to make them always two digits, just insert 02 into the relevant format specifiers, thus:

    String humanReadableElapsedTime = String.format(
            "%d hours, %02d mins, %02d seconds",
            elapsedTime.toHours(),
            elapsedTime.toMinutesPart(),
            elapsedTime.toSecondsPart());

Now we can have for example 0 hours, 39 mins, 09 seconds.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • Any formatting to remove hours/mins/seconds when a preceding number is 0? I mean it is achievable with if else condition, but what about existence of such String formatting or something close, is there any? – Farid Jul 11 '18 at 10:28
  • 1
    No, sorry, @FARID, you need `if`-`else` there. – Ole V.V. Jul 11 '18 at 10:31
  • for Android you will need (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) – Mostafa Imani Apr 12 '22 at 06:35
  • @MostafaImani It works for lower level Android too. For versions before Oreo (APi level 26) with [coreLibraryDesugaring](https://developer.android.com/studio/write/java8-support-table) you can use java.time directly. – Ole V.V. Apr 12 '22 at 06:53
4

For small times, less than an hour, I prefer:

long millis = ...

System.out.printf("%1$TM:%1$TS", millis);
// or
String str = String.format("%1$TM:%1$TS", millis);

for longer intervalls:

private static final long HOUR = TimeUnit.HOURS.toMillis(1);
...
if (millis < HOUR) {
    System.out.printf("%1$TM:%1$TS%n", millis);
} else {
    System.out.printf("%d:%2$TM:%2$TS%n", millis / HOUR, millis % HOUR);
}
user85421
  • 28,957
  • 10
  • 64
  • 87
  • That's a precious hint, there are many such possibilities for date and time with Java Formatter API (https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html)! You could also have the else condition result with: System.out.printf("%1$tH:%1$tM:%1$tS%n", millis); or even System.out.printf("%1$tT%n", millis); – Jose Tepedino Feb 14 '18 at 11:59
  • @JoseTepedino but you are aware that `%tH` **only** shows from 0 up to 23 hours ? That means that for example 24 hours would be displayed as `00`, 25 hours as `01`... same valid for `%tT` - days will be silently discarded. Sure, could also have days displayed, but that would be an overkill for the problem I had when I wrote this (back in 2011) [:-) – user85421 Feb 14 '18 at 12:50
  • I see... that would really only work for times less than a day (24h). Thank you. – Jose Tepedino Feb 14 '18 at 13:16
3

If you know the time difference would be less than an hour, then you can use following code:

    Calendar c1 = Calendar.getInstance();
    Calendar c2 = Calendar.getInstance();

    c2.add(Calendar.MINUTE, 51);

    long diff = c2.getTimeInMillis() - c1.getTimeInMillis();

    c2.set(Calendar.MINUTE, 0);
    c2.set(Calendar.HOUR, 0);
    c2.set(Calendar.SECOND, 0);

    DateFormat df = new SimpleDateFormat("mm:ss");
    long diff1 = c2.getTimeInMillis() + diff;
    System.out.println(df.format(new Date(diff1)));

It will result to: 51:00

Gourav Singla
  • 1,728
  • 1
  • 15
  • 22
3

This answer is similar to some answers above. However, I feel that it would be beneficial because, unlike other answers, this will remove any extra commas or whitespace and handles abbreviation.

/**
 * Converts milliseconds to "x days, x hours, x mins, x secs"
 * 
 * @param millis
 *            The milliseconds
 * @param longFormat
 *            {@code true} to use "seconds" and "minutes" instead of "secs" and "mins"
 * @return A string representing how long in days/hours/minutes/seconds millis is.
 */
public static String millisToString(long millis, boolean longFormat) {
    if (millis < 1000) {
        return String.format("0 %s", longFormat ? "seconds" : "secs");
    }
    String[] units = {
            "day", "hour", longFormat ? "minute" : "min", longFormat ? "second" : "sec"
    };
    long[] times = new long[4];
    times[0] = TimeUnit.DAYS.convert(millis, TimeUnit.MILLISECONDS);
    millis -= TimeUnit.MILLISECONDS.convert(times[0], TimeUnit.DAYS);
    times[1] = TimeUnit.HOURS.convert(millis, TimeUnit.MILLISECONDS);
    millis -= TimeUnit.MILLISECONDS.convert(times[1], TimeUnit.HOURS);
    times[2] = TimeUnit.MINUTES.convert(millis, TimeUnit.MILLISECONDS);
    millis -= TimeUnit.MILLISECONDS.convert(times[2], TimeUnit.MINUTES);
    times[3] = TimeUnit.SECONDS.convert(millis, TimeUnit.MILLISECONDS);
    StringBuilder s = new StringBuilder();
    for (int i = 0; i < 4; i++) {
        if (times[i] > 0) {
            s.append(String.format("%d %s%s, ", times[i], units[i], times[i] == 1 ? "" : "s"));
        }
    }
    return s.toString().substring(0, s.length() - 2);
}

/**
 * Converts milliseconds to "x days, x hours, x mins, x secs"
 * 
 * @param millis
 *            The milliseconds
 * @return A string representing how long in days/hours/mins/secs millis is.
 */
public static String millisToString(long millis) {
    return millisToString(millis, false);
}
Jared Rummler
  • 37,824
  • 19
  • 133
  • 148
3

There is a problem. When milliseconds is 59999, actually it is 1 minute but it will be computed as 59 seconds and 999 milliseconds is lost.

Here is a modified version based on previous answers, which can solve this loss:

public static String formatTime(long millis) {
    long seconds = Math.round((double) millis / 1000);
    long hours = TimeUnit.SECONDS.toHours(seconds);
    if (hours > 0)
        seconds -= TimeUnit.HOURS.toSeconds(hours);
    long minutes = seconds > 0 ? TimeUnit.SECONDS.toMinutes(seconds) : 0;
    if (minutes > 0)
        seconds -= TimeUnit.MINUTES.toSeconds(minutes);
    return hours > 0 ? String.format("%02d:%02d:%02d", hours, minutes, seconds) : String.format("%02d:%02d", minutes, seconds);
}
Hexise
  • 1,520
  • 15
  • 20
3

for correct strings ("1hour, 3sec", "3 min" but not "0 hour, 0 min, 3 sec") i write this code:

int seconds = (int)(millis / 1000) % 60 ;
int minutes = (int)((millis / (1000*60)) % 60);
int hours = (int)((millis / (1000*60*60)) % 24);
int days = (int)((millis / (1000*60*60*24)) % 365);
int years = (int)(millis / 1000*60*60*24*365);

ArrayList<String> timeArray = new ArrayList<String>();

if(years > 0)   
    timeArray.add(String.valueOf(years)   + "y");

if(days > 0)    
    timeArray.add(String.valueOf(days) + "d");

if(hours>0)   
    timeArray.add(String.valueOf(hours) + "h");

if(minutes>0) 
    timeArray.add(String.valueOf(minutes) + "min");

if(seconds>0) 
    timeArray.add(String.valueOf(seconds) + "sec");

String time = "";
for (int i = 0; i < timeArray.size(); i++) 
{
    time = time + timeArray.get(i);
    if (i != timeArray.size() - 1)
        time = time + ", ";
}

if (time == "")
  time = "0 sec";
MyKuLLSKI
  • 5,285
  • 3
  • 20
  • 39
John
  • 1,447
  • 15
  • 16
2

I have covered this in another answer but you can do:

public static Map<TimeUnit,Long> computeDiff(Date date1, Date date2) {
    long diffInMillies = date2.getTime() - date1.getTime();
    List<TimeUnit> units = new ArrayList<TimeUnit>(EnumSet.allOf(TimeUnit.class));
    Collections.reverse(units);
    Map<TimeUnit,Long> result = new LinkedHashMap<TimeUnit,Long>();
    long milliesRest = diffInMillies;
    for ( TimeUnit unit : units ) {
        long diff = unit.convert(milliesRest,TimeUnit.MILLISECONDS);
        long diffInMilliesForUnit = unit.toMillis(diff);
        milliesRest = milliesRest - diffInMilliesForUnit;
        result.put(unit,diff);
    }
    return result;
}

The output is something like Map:{DAYS=1, HOURS=3, MINUTES=46, SECONDS=40, MILLISECONDS=0, MICROSECONDS=0, NANOSECONDS=0}, with the units ordered.

It's up to you to figure out how to internationalize this data according to the target locale.

Community
  • 1
  • 1
Sebastien Lorber
  • 89,644
  • 67
  • 288
  • 419
2

DurationFormatUtils.formatDurationHMS(long)

TOUDIdel
  • 1,322
  • 14
  • 22
2

This topic has been well covered, I just wanted to share my functions perhaps you can make use of these rather than importing an entire library.

    public long getSeconds(ms) {
        return (ms/1000%60);
    }
    public long getMinutes(ms) {
        return (ms/(1000*60)%60);
    }
    public long getHours(ms) {
        return ((ms/(1000*60*60))%24);
    }
derpdewp
  • 182
  • 7
1

I modified @MyKuLLSKI 's answer and added plurlization support. I took out seconds because I didn't need them, though feel free to re-add it if you need it.

public static String intervalToHumanReadableTime(int intervalMins) {

    if(intervalMins <= 0) {
        return "0";
    } else {

        long intervalMs = intervalMins * 60 * 1000;

        long days = TimeUnit.MILLISECONDS.toDays(intervalMs);
        intervalMs -= TimeUnit.DAYS.toMillis(days);
        long hours = TimeUnit.MILLISECONDS.toHours(intervalMs);
        intervalMs -= TimeUnit.HOURS.toMillis(hours);
        long minutes = TimeUnit.MILLISECONDS.toMinutes(intervalMs);

        StringBuilder sb = new StringBuilder(12);

        if (days >= 1) {
            sb.append(days).append(" day").append(pluralize(days)).append(", ");
        }

        if (hours >= 1) {
            sb.append(hours).append(" hour").append(pluralize(hours)).append(", ");
        }

        if (minutes >= 1) {
            sb.append(minutes).append(" minute").append(pluralize(minutes));
        } else {
            sb.delete(sb.length()-2, sb.length()-1);
        }

        return(sb.toString());          

    }

}

public static String pluralize(long val) {
    return (Math.round(val) > 1 ? "s" : "");
}
bitstream
  • 459
  • 9
  • 20
1

Use java.util.concurrent.TimeUnit, and use this simple method:

private static long timeDiff(Date date, Date date2, TimeUnit unit) {
    long milliDiff=date2.getTime()-date.getTime();
    long unitDiff = unit.convert(milliDiff, TimeUnit.MILLISECONDS);
    return unitDiff; 
}

For example:

SimpleDateFormat sdf = new SimpleDateFormat("yy/MM/dd HH:mm:ss");  
Date firstDate = sdf.parse("06/24/2017 04:30:00");
Date secondDate = sdf.parse("07/24/2017 05:00:15");
Date thirdDate = sdf.parse("06/24/2017 06:00:15");

System.out.println("days difference: "+timeDiff(firstDate,secondDate,TimeUnit.DAYS));
System.out.println("hours difference: "+timeDiff(firstDate,thirdDate,TimeUnit.HOURS));
System.out.println("minutes difference: "+timeDiff(firstDate,thirdDate,TimeUnit.MINUTES));
System.out.println("seconds difference: "+timeDiff(firstDate,thirdDate,TimeUnit.SECONDS));