199

I'm confused. After stumbling upon this thread, I tried to figure out how to format a countdown timer that had the format hh:mm:ss.

Here's my attempt -

//hh:mm:ss
String.format("%02d:%02d:%02d", 
    TimeUnit.MILLISECONDS.toHours(millis),
    TimeUnit.MILLISECONDS.toMinutes(millis) - 
    TimeUnit.MINUTES.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)),
    TimeUnit.MILLISECONDS.toSeconds(millis) - 
    TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));   

So, when I try a value like 3600000ms, I get 01:59:00, which is wrong since it should be 01:00:00. Obviously there's something wrong with my logic, but at the moment, I cannot see what it is!

Can anyone help?

Edit -

Fixed it. Here's the right way to format milliseconds to hh:mm:ss format -

//hh:mm:ss
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))));

The problem was this TimeUnit.MINUTES.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)). It should have been this TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)) instead.

Community
  • 1
  • 1
mre
  • 40,416
  • 33
  • 117
  • 162
  • 1
    3,600,000 milliseconds is 3,600 seconds, or 60 minutes, or 1 hour. It shouldn't be `00:59:59`, it should be `01:00:00`. – Borealid Jan 27 '12 at 00:05

17 Answers17

381

You were really close:

String.format("%02d:%02d:%02d", 
TimeUnit.MILLISECONDS.toHours(millis),
TimeUnit.MILLISECONDS.toMinutes(millis) -  
TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)), // The change is in this line
TimeUnit.MILLISECONDS.toSeconds(millis) - 
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));   

You were converting hours to millisseconds using minutes instead of hours.

BTW, I like your use of the TimeUnit API :)

Here's some test code:

public static void main(String[] args) throws ParseException {
    long millis = 3600000;
    String hms = 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)));
    System.out.println(hms);
}

Output:

01:00:00

I realised that my code above can be greatly simplified by using a modulus division instead of subtraction:

String hms = String.format("%02d:%02d:%02d", TimeUnit.MILLISECONDS.toHours(millis),
    TimeUnit.MILLISECONDS.toMinutes(millis) % TimeUnit.HOURS.toMinutes(1),
    TimeUnit.MILLISECONDS.toSeconds(millis) % TimeUnit.MINUTES.toSeconds(1));

Still using the TimeUnit API for all magic values, and gives exactly the same output.

Bohemian
  • 365,064
  • 84
  • 522
  • 658
  • 2
    Amen, like the way you use TimeUnit API :) – Dheeraj Bhaskar Jan 12 '13 at 13:27
  • 2
    It is not a good idea to reinvent the wheel! Triton Man's answer is better solution. – محمدباقر Jul 10 '13 at 06:10
  • 6
    @محمدباقر But the other answer uses an external library, whereas my code uses the core JDK only. It depends on what your needs are - for example you may not be able to use an external library. The other answer is good though. – Bohemian Jul 10 '13 at 07:39
  • How 'bout when it exceeds 365 days? Can you do the equivalent years? Thanks. – KarenAnne Oct 15 '13 at 08:23
  • @KarenAnne what do you mean? Can you give sample input and output please? – Bohemian Oct 15 '13 at 08:36
  • @محمدباقر I needed to add Days and extra text to the string. in this answer, easily i inserted my text between the %02d and finished, what about doing this with the answer you suggested is better than this ? – MBH Jun 05 '16 at 20:28
  • Given time in milliseconds, say the time is not upto an hour, it returns for example "00:15:23". Is there any way to make it remove zeros if empty? For example to return "15:23" instead of "00:15:23". – X09 Apr 10 '17 at 02:28
  • 2
    @Ozuf add `.replaceAll("^00:", "")` or `.replaceAll("^00:(00:)?", "")` to remove minutes as well as hours if both are zero. – Bohemian Apr 10 '17 at 02:50
  • Thanks but what happens if the time is "00:15:00" or "10:00:23"? – X09 Apr 10 '17 at 02:55
  • 2
    @Ozuf you get `"15:00"` and `"10:00:23"` respectively; only *leading* zeros are removed, due to the match being anchored to start of input by `^`. – Bohemian Apr 10 '17 at 02:57
  • Thanks a million :D. Please can you update the answer so that others can easily see it? – X09 Apr 10 '17 at 02:58
  • int hrs = (int) TimeUnit.MILLISECONDS.toHours(miliSeconds) % 24; int min = (int) TimeUnit.MILLISECONDS.toMinutes(miliSeconds) % 60; int sec = (int) TimeUnit.MILLISECONDS.toSeconds(miliSeconds) % 60; return String.format("%02d:%02d:%02d", hrs, min, sec); – Swapnil Nov 06 '18 at 18:20
  • Saved lot of my time. Thanks. – uncommon_breed Mar 04 '19 at 18:32
81

The generic method for this is fairly simple:

public static String convertSecondsToHMmSs(long seconds) {
    long s = seconds % 60;
    long m = (seconds / 60) % 60;
    long h = (seconds / (60 * 60)) % 24;
    return String.format("%d:%02d:%02d", h,m,s);
}
Arets Paeglis
  • 3,588
  • 3
  • 31
  • 42
65

If you are using apache commons:

DurationFormatUtils.formatDuration(timeInMS, "HH:mm:ss,SSS");
Rocky Pulley
  • 20,107
  • 18
  • 63
  • 98
28

I used this:

String.format("%1$tH:%1$tM:%1$tS.%1$tL", millis);

See description of class Formatter.

See runnable example using input of 2400 ms.

Derek Mahar
  • 25,458
  • 37
  • 115
  • 164
ZuluM
  • 281
  • 3
  • 2
  • It seems not working, I simply test it by pass 100, 1000, 1200 and it responding as _05:30:00_ , _05:30:01_, _05:30:01_. – CoDe Oct 24 '17 at 01:36
  • 2
    @CoDe, it works for your time zone. You must be in a different time zone than UTC. `String.format()` is time zone _dependent_. The ideal correct answer, however, should be time zone _independent_. – Derek Mahar Jul 05 '18 at 18:38
  • @CoDe, it is showing times that correspond to your time zone which must be different than UTC. `String.format()` is time zone _dependent_. The ideal correct answer, however, should be time zone _independent_. – Derek Mahar Jul 06 '18 at 14:26
25
// New date object from millis
Date date = new Date(millis);
// formattter 
SimpleDateFormat formatter= new SimpleDateFormat("HH:mm:ss.SSS");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
// Pass date object
String formatted = formatter.format(date );

See runnable example using input of 1200 ms.

Derek Mahar
  • 25,458
  • 37
  • 115
  • 164
Vinay Lodha
  • 1,997
  • 19
  • 29
  • 3
    This would produce incorrect results for amounts of time more than a day. It would wrap the number of hours from 23 back to 0, which probably isn't desirable here. – Kenster Jun 20 '14 at 12:44
  • No buddy... I think it would still work the same. try with today date – Vinay Lodha Jun 23 '14 at 04:23
  • 1
    SimpleDateFormat sdf = new SimpleDateFormat("hh:mm aa"); in case someone wants in 12 hours format with am/pm – Ajji May 23 '18 at 16:43
14
DateFormat df = new SimpleDateFormat("HH:mm:ss");
String formatted = df.format(aDateObject);
Gabriel Belingueres
  • 2,819
  • 1
  • 21
  • 29
  • 1
    Same answer as http://stackoverflow.com/questions/9027317/how-to-convert-milliseconds-to-hhmmss-format/24327049#24327049 with same problem for duration above 1 day – OneWorld Feb 24 '16 at 10:36
  • It's also dependent on time zone which means the result will differ when run in different time zones. – Derek Mahar Jul 06 '18 at 14:27
11

Test results for the 4 implementations

Having to do a lot of formatting for huge data, needed the best performance, so here are the (surprising) results:

for (int i = 0; i < 1000000; i++) { FUNCTION_CALL }

Durations:

  • combinationFormatter: 196 millis
  • formatDuration: 272 millis
  • apacheFormat: 754 millis
  • formatTimeUnit: 2216 millis

    public static String apacheFormat(long millis) throws ParseException {
        return DurationFormatUtils.formatDuration(millis, "HH:mm:ss");
    }
    
    public static String formatTimeUnit(long millis) throws ParseException {
    String formatted = 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)));
        return formatted;
    }
    
    public static String formatDuration(final long millis) {
        long seconds = (millis / 1000) % 60;
        long minutes = (millis / (1000 * 60)) % 60;
        long hours = millis / (1000 * 60 * 60);
    
        StringBuilder b = new StringBuilder();
        b.append(hours == 0 ? "00" : hours < 10 ? String.valueOf("0" + hours) : 
        String.valueOf(hours));
        b.append(":");
        b.append(minutes == 0 ? "00" : minutes < 10 ? String.valueOf("0" + minutes) :     
        String.valueOf(minutes));
        b.append(":");
        b.append(seconds == 0 ? "00" : seconds < 10 ? String.valueOf("0" + seconds) : 
        String.valueOf(seconds));
        return b.toString();
    }
    
    public static String combinationFormatter(final long millis) {
        long seconds = TimeUnit.MILLISECONDS.toSeconds(millis)
                - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis));
        long minutes = TimeUnit.MILLISECONDS.toMinutes(millis)
                - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis));
        long hours = TimeUnit.MILLISECONDS.toHours(millis);
    
        StringBuilder b = new StringBuilder();
        b.append(hours == 0 ? "00" : hours < 10 ? String.valueOf("0" + hours) : 
        String.valueOf(hours));
        b.append(":");
        b.append(minutes == 0 ? "00" : minutes < 10 ? String.valueOf("0" + minutes) : 
        String.valueOf(minutes));
            b.append(":");
        b.append(seconds == 0 ? "00" : seconds < 10 ? String.valueOf("0" + seconds) : 
        String.valueOf(seconds));
        return b.toString(); 
     }
    
MariusA
  • 119
  • 1
  • 5
  • how reliable are metrics like these? I've never really understood the process. – mre Feb 11 '14 at 14:31
  • In my situation where some thousands of objects are created in succession, this test I think is pretty useful. Okay not setting just some strings, but creating objects which have one field a formatted string. – MariusA Feb 11 '14 at 23:40
  • @marius you went to the trouble to use `StringBuilder` then make the mistake of using "0"+number kind of "ruins" it. @mre JVM's GC is extremely good at allocating and collecting lots of short-lived objects, no worries there. I went with `String.format` no leading-zero logic to deal with. – escape-llc Jan 14 '16 at 16:30
9

this worked for me, with kotlin

fun formatToDigitalClock(miliSeconds: Long): String {
        val hours = TimeUnit.MILLISECONDS.toHours(miliSeconds).toInt() % 24
        val minutes = TimeUnit.MILLISECONDS.toMinutes(miliSeconds).toInt() % 60
        val seconds = TimeUnit.MILLISECONDS.toSeconds(miliSeconds).toInt() % 60
        return when {
            hours > 0 -> String.format("%d:%02d:%02d", hours, minutes, seconds)
            minutes > 0 -> String.format("%02d:%02d", minutes, seconds)
            seconds > 0 -> String.format("00:%02d", seconds)
            else -> {
                "00:00"
            }
        }
    }
Devix
  • 335
  • 4
  • 13
8

Java 9

    Duration timeLeft = Duration.ofMillis(3600000);
    String hhmmss = String.format("%02d:%02d:%02d", 
            timeLeft.toHours(), timeLeft.toMinutesPart(), timeLeft.toSecondsPart());
    System.out.println(hhmmss);

This prints:

01:00:00

You are doing right in letting library methods do the conversions involved for you. java.time, the modern Java date and time API, or more precisely, its Duration class does it more elegantly and in a less error-prone way than TimeUnit.

The toMinutesPart and toSecondsPart methods I used were introduced in Java 9.

Java 6, 7 and 8

    long hours = timeLeft.toHours();
    timeLeft = timeLeft.minusHours(hours);
    long minutes = timeLeft.toMinutes();
    timeLeft = timeLeft.minusMinutes(minutes);
    long seconds = timeLeft.toSeconds();
    String hhmmss = String.format("%02d:%02d:%02d", hours, minutes, seconds);
    System.out.println(hhmmss);

The output is the same as above.

Question: How can that work in Java 6 and 7?

  • In Java 8 and later and on newer Android devices (from API level 26, I’m told) java.time comes built-in.
  • In Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
  • On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.

Links

KhogaEslam
  • 1,383
  • 16
  • 18
Ole V.V.
  • 65,573
  • 11
  • 96
  • 117
7

The answer marked as correct has a little mistake,

String myTime = String.format("%02d:%02d:%02d",
            TimeUnit.MILLISECONDS.toHours(millis),
            TimeUnit.MILLISECONDS.toMinutes(millis) -
                    TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)), // The change is in this line
            TimeUnit.MILLISECONDS.toSeconds(millis) -
                    TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));

for example this is an example of the value that i get:

417474:44:19

This is the solution to get the right format is:

String myTime =  String.format("%02d:%02d:%02d",
                //Hours
                TimeUnit.MILLISECONDS.toHours(millis) -
                        TimeUnit.DAYS.toHours(TimeUnit.MILLISECONDS.toDays(millis)),
                //Minutes
                TimeUnit.MILLISECONDS.toMinutes(millis) -
                        TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)),
                //Seconds
                TimeUnit.MILLISECONDS.toSeconds(millis) -
                        TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));

getting as a result a correct format:

18:44:19

other option to get the format hh:mm:ss is just :

   Date myDate = new Date(timeinMillis);
   SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
   String myTime = formatter.format(myDate);
Jorgesys
  • 114,263
  • 22
  • 306
  • 247
6

Going by Bohemian's answer we need need not use TimeUnit to find a known value. Much more optimal code would be

String hms = String.format("%02d:%02d:%02d", millisLeft/(3600*1000),
                    millisLeft/(60*1000) % 60,
                    millisLeft/1000 % 60);

Hope it helps

Aalin
  • 86
  • 1
  • 3
6
        String string = String.format("%02d:%02d:%02d.%03d",
            TimeUnit.MILLISECONDS.toHours(millisecend), TimeUnit.MILLISECONDS.toMinutes(millisecend) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millisecend)),
            TimeUnit.MILLISECONDS.toSeconds(millisecend) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millisecend)), millisecend - TimeUnit.SECONDS.toMillis(TimeUnit.MILLISECONDS.toSeconds(millisecend)));

Format: 00:00:00.000

Example: 615605 Millisecend

00:10:15.605

Omid Naji
  • 139
  • 1
  • 11
6
 public String millsToDateFormat(long mills) {

    Date date = new Date(mills);
    DateFormat formatter = new SimpleDateFormat("HH:mm:ss");
    String dateFormatted = formatter.format(date);
    return dateFormatted; //note that it will give you the time in GMT+0
}
ctu
  • 188
  • 2
  • 12
5

The code below does the conversion in both way

23:59:58:999 to 86398999

and than

86398999 to 23:59:58:999


import java.util.concurrent.TimeUnit;

public class TimeUtility {

    public static void main(String[] args) {

        long currentDateTime = System.currentTimeMillis();

        String strTest = "23:59:58:999";
        System.out.println(strTest);

        long l = strToMilli(strTest);
        System.out.println(l);
        l += 1;
        String str = milliToString(l);
        System.out.println(str);
    }

    /**
     * convert a time string into the equivalent long milliseconds
     *
     * @param strTime string fomratted as HH:MM:SS:MSMS i.e. "23:59:59:999"
     * @return long integer like 86399999
     */
    public static long strToMilli(String strTime) {
        long retVal = 0;
        String hour = strTime.substring(0, 2);
        String min = strTime.substring(3, 5);
        String sec = strTime.substring(6, 8);
        String milli = strTime.substring(9, 12);
        int h = Integer.parseInt(hour);
        int m = Integer.parseInt(min);
        int s = Integer.parseInt(sec);
        int ms = Integer.parseInt(milli);

        String strDebug = String.format("%02d:%02d:%02d:%03d", h, m, s, ms);
        //System.out.println(strDebug);
        long lH = h * 60 * 60 * 1000;
        long lM = m * 60 * 1000;
        long lS = s * 1000;

        retVal = lH + lM + lS + ms;
        return retVal;
    }

    /**
     * convert time in milliseconds to the corresponding string, in case of day
     * rollover start from scratch 23:59:59:999 + 1 = 00:00:00:000
     *
     * @param millis the number of milliseconds corresponding to tim i.e.
     *               34137999 that can be obtained as follows;
     *               <p>
     *               long lH = h * 60 * 60 * 1000; //hour to milli
     *               <p>
     *               long lM = m * 60 * 1000; // minute to milli
     *               <p>
     *               long lS = s * 1000; //seconds to milli
     *               <p>
     *               millis = lH + lM + lS + ms;
     * @return a string formatted as HH:MM:SS:MSMS i.e. "23:59:59:999"
     */
    private static String milliToString(long millis) {

        long hrs = TimeUnit.MILLISECONDS.toHours(millis) % 24;
        long min = TimeUnit.MILLISECONDS.toMinutes(millis) % 60;
        long sec = TimeUnit.MILLISECONDS.toSeconds(millis) % 60;
        //millis = millis - (hrs * 60 * 60 * 1000); //alternative way
        //millis = millis - (min * 60 * 1000);
        //millis = millis - (sec * 1000);
        //long mls = millis ;
        long mls = millis % 1000;
        String toRet = String.format("%02d:%02d:%02d:%03d", hrs, min, sec, mls);
        //System.out.println(toRet);
        return toRet;
    }
}
beresfordt
  • 4,713
  • 7
  • 32
  • 41
  • I upvoted this solution. Just put a dot between sec and mls. And I don't know why times and date handling is sooooo difficult in Java. The most used functions should be already available and part of the class. – Baruch Atta Mar 05 '18 at 18:05
4

I tried as shown in the first answer. It works, but minus brought me into confusion. My answer by Groovy:

import static java.util.concurrent.TimeUnit.*

...

private static String formatElapsedTime(long millis) {

    int hrs = MILLISECONDS.toHours(millis) % 24
    int min = MILLISECONDS.toMinutes(millis) % 60
    int sec = MILLISECONDS.toSeconds(millis) % 60
    int mls = millis % 1000

    sprintf( '%02d:%02d:%02d (%03d)', [hrs, min, sec, mls])
}
leonov
  • 41
  • 2
3

For Kotlin

val hours = String.format("%02d", TimeUnit.MILLISECONDS.toHours(milSecs))
val minutes = String.format("%02d",
                        TimeUnit.MILLISECONDS.toMinutes(milSecs) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(milSecs)))
val seconds = String.format("%02d",
                        TimeUnit.MILLISECONDS.toSeconds(milSecs) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(milSecs)))

where, milSecs is milliseconds

Kishan Solanki
  • 8,893
  • 2
  • 51
  • 54
1

Well, you could try something like this, :

public String getElapsedTimeHoursMinutesSecondsString() {       
     long elapsedTime = getElapsedTime();  
     String format = String.format("%%0%dd", 2);  
     elapsedTime = elapsedTime / 1000;  
     String seconds = String.format(format, elapsedTime % 60);  
     String minutes = String.format(format, (elapsedTime % 3600) / 60);  
     String hours = String.format(format, elapsedTime / 3600);  
     String time =  hours + ":" + minutes + ":" + seconds;  
     return time;  
 }  

to convert milliseconds to a time value

anonymous
  • 1,081
  • 1
  • 10
  • 18