Como converter milissegundos para o formato "hh: mm: ss"?

184

Estou confuso. Depois de tropeçar nessa discussão, tentei descobrir como formatar um cronômetro de contagem regressiva que tivesse o formato hh:mm:ss.

Aqui está a minha tentativa -

//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)));   

Então, quando tento um valor como 3600000ms, recebo 01:59:00, o que está errado, pois deveria estar 01:00:00. Obviamente, há algo errado com minha lógica, mas, no momento, não consigo ver o que é!

Alguém pode ajudar?

Editar -

Corrigido. Aqui está a maneira correta de formatar milissegundos para hh:mm:ssformatar:

//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))));

O problema era esse TimeUnit.MINUTES.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)). Deveria ter sido isso TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)).

senhor
fonte
1
3.600.000 milissegundos são 3.600 segundos ou 60 minutos ou 1 hora. Não deveria ser 00:59:59, deveria ser 01:00:00.
Borealid 27/01

Respostas:

355

Você estava realmente perto:

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

Você estava convertendo horas em milissegundos usando minutos vez de horas .

BTW, eu gosto do seu uso da TimeUnitAPI :)

Aqui está um código de teste:

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);
}

Resultado:

01:00:00

Percebi que meu código acima pode ser bastante simplificado usando uma divisão de módulo em vez de subtração:

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

Ainda está usando a TimeUnitAPI para todos os valores mágicos e fornece exatamente a mesma saída.

Boêmio
fonte
2
Amém, assim como a maneira como você usa TimeUnit API :)
Dheeraj Bhaskar
2
Não é uma boa ideia reinventar a roda! A resposta de Triton Man é a melhor solução.
10133 #
5
@ محمدباقر Mas a outra resposta usa uma biblioteca externa, enquanto meu código usa apenas o JDK principal. Depende de quais são suas necessidades - por exemplo, você pode não conseguir usar uma biblioteca externa. A outra resposta é boa.
Bohemian
2
@Ozuf adicione .replaceAll("^00:", "")ou .replaceAll("^00:(00:)?", "")remova minutos e horas, se ambos forem zero.
Bohemian
2
@ Ozuf você recebe "15:00"e, "10:00:23"respectivamente; somente zeros à esquerda são removidos devido à ancoragem da correspondência no início da entrada ^.
Bohemian
77

O método genérico para isso é bastante simples:

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
fonte
1
@ Javaman59, você quis dizer String.format("% 02 d:%02d:%02d", h,m,s) ?
KarenAnne
2
@KarenAnne. Isso depende se você deseja, por exemplo, "03:59:59" ou "3:59:59".
Stephen Hosking 19/10/2013
2
O tempo está em milissegundos, você precisa multiplicar segundos por 1000
Apurva
5
autor perguntou sobre milissegundos, não segundos!
user924
59

Se você estiver usando o apache commons:

DurationFormatUtils.formatDuration(timeInMS, "HH:mm:ss,SSS");
Triton Man
fonte
4
Atalho:DurationFormatUtils.formatDurationHMS(timeInMS);
Aroniaina
@Jorgesys Por que eles estão obsoletos?
Piovezan
eles não são obsoletos.
Triton Man
26

Eu usei isso:

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

Veja a descrição da classe Formatter .

Veja o exemplo executável usando a entrada de 2400 ms.

ZuluM
fonte
Parece que não está funcionando, eu simplesmente testei com aprovação 100, 1000, 1200 e respondi como 05:30:00 , 05:30:01 , 05:30:01 .
CoDe
2
@CoDe, funciona para o seu fuso horário. Você deve estar em um fuso horário diferente do UTC. String.format()é fuso horário dependente . A resposta correta ideal, no entanto, deve ser independente do fuso horário .
Derek Mahar
@CoDe, está mostrando horários que correspondem ao seu fuso horário e devem ser diferentes do UTC. String.format()é o fuso horário dependente . A resposta correta ideal, no entanto, deve ser independente do fuso horário .
Derek Mahar
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 );

Veja o exemplo executável usando entrada de 1200 ms.

Vinay Lodha
fonte
3
Isso produziria resultados incorretos por períodos de tempo superiores a um dia. Ele agruparia o número de horas de 23 a 0, o que provavelmente não é desejável aqui.
Kenster
Nenhum amigo ... acho que ainda funcionaria da mesma maneira. tente com hoje data
Vinay Lodha
1
SimpleDateFormat sdf = novo SimpleDateFormat ("hh: mm aa"); no caso de alguém quiser em 12 horas formato com am / pm
Ajji
14
DateFormat df = new SimpleDateFormat("HH:mm:ss");
String formatted = df.format(aDateObject);
Gabriel Belingueres
fonte
1
Mesma resposta que stackoverflow.com/questions/9027317/… com o mesmo problema para duração acima de 1 dia
OneWorld 24/02
Também depende do fuso horário, o que significa que o resultado será diferente quando executado em diferentes fusos horários.
Derek Mahar
10

Resultados do teste para as 4 implementações

Tendo que fazer muita formatação para obter grandes dados, precisava do melhor desempenho, então aqui estão os resultados (surpreendentes):

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

Durações:

  • compositionFormatter : 196 milhões
  • formatoDuração: 272 milhões
  • apacheFormat: 754 milhões
  • formatTimeUnit: 2216 milis

    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
fonte
Quão confiáveis ​​são essas métricas? Eu realmente nunca entendi o processo.
MRE
Na minha situação em que alguns milhares de objetos são criados em sucessão, acho que esse teste é bastante útil. Ok, não definindo apenas algumas strings, mas criando objetos que possuem um campo em uma string formatada.
Mariusa
@ marius você se deu ao trabalho de usar StringBuildere cometeu o erro de usar "0" + número de "ruínas" dele. O GC da @VM JVM é extremamente bom em alocar e coletar muitos objetos de vida curta, não se preocupe. Eu fui com String.formatnenhuma lógica de zero-zero para lidar.
escape-llc
7

isso funcionou para mim, com 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
fonte
Funcionou perfeitamente para mim. Esta deve ser a melhor resposta, uma vez que lida com zero hora
vNext
6

Seguindo a resposta de Bohemian, não precisamos usar o TimeUnit para encontrar um valor conhecido. Um código muito mais ideal seria

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

Espero que ajude

Aalin
fonte
Como a sua resposta mais! Apenas matemática simples. Aliás, no meu caso, adicionei os milissegundos ao final millisLeft%1000/10para obter o formato #hour:minutes:seconds:milliseconds
Lê Quang Duy
@ LêQuangDuy eu preciso de milissegundos em 3 dígitos como 01: 24: 51,123, mas você está cortando 3 no final o que fazer?
naanu 19/06
@naanu basta usarmillisLeft%1000
Lê Quang Duy
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
fonte
A resposta depende do fuso horário, o que significa que o resultado será diferente quando executado em diferentes fusos horários.
Derek Mahar
6

Java 9

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

Isso imprime:

01:00:00

Você está fazendo certo ao permitir que os métodos da biblioteca façam as conversões envolvidas para você. java.time, a moderna API de data e hora do Java, ou mais precisamente, sua Durationclasse o faz de maneira mais elegante e menos propensa a erros do que TimeUnit.

Os métodos toMinutesParte toSecondsPartque eu usei foram introduzidos no Java 9.

Java 6, 7 e 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);

A saída é a mesma que acima.

Pergunta: Como isso pode funcionar no Java 6 e 7?

  • No Java 8 e posterior, e em dispositivos Android mais recentes (do nível 26 da API, pelo que me disseram), ele java.timevem embutido.
  • No Java 6 e 7, obtenha o ThreeTen Backport, o backport das classes modernas (ThreeTen para JSR 310; veja os links na parte inferior).
  • No Android (mais antigo), use a edição Android do ThreeTen Backport. Chama-se ThreeTenABP. E certifique-se de importar as classes de data e hora deorg.threeten.bp com subpacotes.

Ligações

Ole VV
fonte
5

O código abaixo faz a conversão nos dois sentidos

23: 59: 58: 999 a 86398999

e então

86398999 a 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;
    }
}
Menzio Luca Fabrizio
fonte
Eu votei nessa solução. Basta colocar um ponto entre segundos e mls. E não sei por que horários e datas são tão difíceis em Java. As funções mais usadas já devem estar disponíveis e fazer parte da classe.
Baruch Atta
5
        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)));

Formato: 00: 00: 00.000

Exemplo: 615605 Milissegundo

00: 10: 15.605

Omid Naji
fonte
5

A resposta marcada como correta tem um pequeno erro,

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

por exemplo, este é um exemplo do valor que recebo:

417474:44:19

Esta é a solução para obter o formato correto é:

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

obtendo como resultado um formato correto:

18:44:19

outra opção para obter o formato hh:mm:ssé apenas:

   Date myDate = new Date(timeinMillis);
   SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
   String myTime = formatter.format(myDate);
Jorgesys
fonte
4

Eu tentei como mostrado na primeira resposta. Funciona, mas menos me trouxe confusão. Minha resposta por 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
fonte
3

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

onde, milSecsé milissegundos

Kishan Solanki
fonte
1

Bem, você pode tentar algo como isto:

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;  
 }  

converter milissegundos em um valor de tempo

anônimo
fonte