¿Cómo convertir milisegundos al formato “hh: mm: ss”?

184

Estoy confundido. Después de tropezar con este hilo, traté de descubrir cómo formatear un temporizador de cuenta regresiva que tenía el formato hh:mm:ss.

Aquí está mi intento:

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

Entonces, cuando intento un valor como 3600000ms, obtengo 01:59:00, lo que está mal, ya que debería ser 01:00:00. Obviamente hay algo mal con mi lógica, pero por el momento, ¡no puedo ver qué es!

¿Alguien puede ayudar?

Editar -

Arreglado. Aquí está la forma correcta de formatear milisegundos para hh:mm:ssformatear:

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

El problema fue este TimeUnit.MINUTES.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)). Debería haber sido esto TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis))en su lugar.

mre
fuente
1
3,600,000 milisegundos es 3,600 segundos, o 60 minutos, o 1 hora. No debería ser 00:59:59, debería ser 01:00:00.
Borealid

Respuestas:

355

Estabas muy cerca:

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

Estaba convirtiendo horas a milisegundos usando minutos en lugar de horas .

Por cierto, me gusta tu uso de la TimeUnitAPI :)

Aquí hay un código de prueba:

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

Salida:

01:00:00

Me di cuenta de que mi código anterior se puede simplificar enormemente usando una división de módulo en lugar de una resta:

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

Sigue usando la TimeUnitAPI para todos los valores mágicos y ofrece exactamente el mismo resultado.

Bohemio
fuente
2
Amén, como la forma en que usas TimeUnit API :)
Dheeraj Bhaskar
2
¡No es una buena idea reinventar la rueda! La respuesta de Triton Man es una mejor solución.
محمدباقر
55
@ محمدباقر Pero la otra respuesta usa una biblioteca externa, mientras que mi código usa solo el núcleo JDK. Depende de cuáles sean sus necesidades; por ejemplo, es posible que no pueda usar una biblioteca externa. Sin embargo, la otra respuesta es buena.
Bohemio
2
@Ozuf agregue .replaceAll("^00:", "")o .replaceAll("^00:(00:)?", "")elimine minutos y horas si ambos son cero.
Bohemio
2
@Ozuf obtienes "15:00"y "10:00:23"respectivamente; Sólo principales ceros se eliminan, debido a la coincidencia de ser anclada al inicio de la entrada por ^.
Bohemio
77

El método genérico para esto es bastante 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
fuente
1
@ Javaman59, ¿te refieres a String.format("% 02 d:%02d:%02d", h,m,s) ?
KarenAnne
2
@KarenAnne. Eso depende de si desea, por ejemplo, "03:59:59" o "3:59:59".
Stephen Hosking
2
El tiempo está en milisegundos, debe multiplicar los segundos con 1000
Apurva
55
¡El autor preguntó sobre milisegundos, no segundos!
usuario924
59

Si está utilizando los bienes comunes de Apache:

DurationFormatUtils.formatDuration(timeInMS, "HH:mm:ss,SSS");
Triton Man
fuente
44
Atajo:DurationFormatUtils.formatDurationHMS(timeInMS);
Aroniaina
@Jorgesys ¿Por qué son obsoletos?
Piovezan
No son obsoletos.
Triton Man
26

Usé esto:

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

Ver descripción de la clase Formatter .

Vea el ejemplo ejecutable usando una entrada de 2400 ms.

ZuluM
fuente
Parece que no funciona, simplemente lo pruebo con el pase 100, 1000, 1200 y responde como 05:30:00 , 05:30:01 , 05:30:01 .
Código
2
@CoDe, funciona para su zona horaria. Debe estar en una zona horaria diferente a UTC. String.format()es zona de tiempo dependiente . Sin embargo, la respuesta correcta ideal debería ser la zona horaria independiente .
Derek Mahar
@CoDe, muestra las horas que corresponden a su zona horaria que deben ser diferentes a UTC. String.format()es zona de tiempo dependiente . Sin embargo, la respuesta correcta ideal debería ser la zona horaria independiente .
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 );

Vea el ejemplo ejecutable usando una entrada de 1200 ms.

Vinay Lodha
fuente
3
Esto produciría resultados incorrectos por períodos de más de un día. Envolvería el número de horas de 23 a 0, lo que probablemente no sea deseable aquí.
Kenster
No amigo ... creo que todavía funcionaría igual. probar con la fecha de hoy
Vinay Lodha
1
SimpleDateFormat sdf = new SimpleDateFormat ("hh: mm aa"); en caso de que alguien quiera en formato de 12 horas con am / pm
Ajji
14
DateFormat df = new SimpleDateFormat("HH:mm:ss");
String formatted = df.format(aDateObject);
Gabriel Belingueres
fuente
1
Misma respuesta que stackoverflow.com/questions/9027317/... con el mismo problema por una duración superior a 1 día
OneWorld
También depende de la zona horaria, lo que significa que el resultado será diferente cuando se ejecute en diferentes zonas horarias.
Derek Mahar
10

Resultados de la prueba para las 4 implementaciones

Tener que formatear muchos datos enormes necesitaba el mejor rendimiento, así que aquí están los resultados (sorprendentes):

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

Duraciones:

  • combinaciónFormatter: 196 millis
  • formatoDuración: 272 milis
  • 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
fuente
¿Qué tan confiables son las métricas como estas? Nunca he entendido realmente el proceso.
mre
En mi situación en la que se crean sucesivamente miles de objetos, esta prueba creo que es bastante útil. Bien, no estableciendo solo algunas cadenas, sino creando objetos que tengan un campo, una cadena formateada.
MariusA
@marius te tomaste la molestia de usar y StringBuilderluego cometiste el error de usar "0" + número de tipo de "ruinas". El GC de @mre JVM es extremadamente bueno en la asignación y recolección de muchos objetos de corta duración, no se preocupe allí. Fui String.formatsin una lógica de cero a la izquierda para tratar.
escape-llc
7

esto funcionó para mí, con 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
fuente
Funcionó perfectamente para mí. Esta debería ser la mejor respuesta ya que maneja la hora cero
vNext
6

Siguiendo la respuesta de Bohemian, no necesitamos usar TimeUnit para encontrar un valor conocido. El código mucho más óptimo sería

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

Espero eso ayude

Aalin
fuente
¡Me gusta más tu respuesta! Solo matemática simple. Por cierto, en mi caso agregué los milisegundos hasta el final millisLeft%1000/10para que obtengamos el formatohour:minutes:seconds:milliseconds
Lê Quang Duy
@ LêQuangDuy necesito milisegundos en 3 dígitos como 01: 24: 51,123 pero tu estás cortando 3 por fin ¿qué hacer?
naanu
@naanu solo usamillisLeft%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
fuente
La respuesta depende de la zona horaria, lo que significa que el resultado será diferente cuando se ejecute en diferentes zonas horarias.
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);

Esto imprime:

01:00:00

Estás haciendo lo correcto al permitir que los métodos de la biblioteca hagan las conversiones involucradas por ti. java.time, la moderna API de fecha y hora de Java, o más precisamente, su Durationclase lo hace de manera más elegante y de una manera menos propensa a errores queTimeUnit .

El toMinutesPartytoSecondsPart los métodos que he utilizado se introdujeron en Java 9.

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

El resultado es el mismo que el anterior.

Pregunta: ¿Cómo puede funcionar eso en Java 6 y 7?

  • En Java 8 y versiones posteriores y en dispositivos Android más nuevos (desde el nivel 26 de API, me han dicho) java.time viene incorporado.
  • En Java 6 y 7, obtenga el Backport ThreeTen, el backport de las clases modernas (ThreeTen para JSR 310; vea los enlaces en la parte inferior).
  • En Android (anterior) use la edición de Android de ThreeTen Backport. Se llama ThreeTenABP. Y asegúrese de importar las clases de fecha y hora org.threeten.bpcon subpaquetes.

Enlaces

Ole VV
fuente
5

El siguiente código realiza la conversión en ambos sentidos.

23: 59: 58: 999 a 86398999

y que

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
fuente
Voté esta solución. Simplemente ponga un punto entre seg y mls. Y no sé por qué el manejo de horas y fechas es tan difícil en Java. Las funciones más utilizadas ya deberían estar disponibles y ser parte de la clase.
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

Ejemplo: 615605 Millisecend

00: 10: 15.605

Omid Naji
fuente
5

La respuesta marcada como correcta tiene un pequeño error,

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 ejemplo, este es un ejemplo del valor que obtengo:

417474:44:19

Esta es la solución para obtener el formato correcto es:

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

obteniendo como resultado un formato correcto:

18:44:19

Otra opción para obtener el formato hh:mm:sses simplemente:

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

Intenté como se muestra en la primera respuesta. Funciona, pero menos me llevó a la confusión. Mi respuesta 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
fuente
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)))

donde, milSecses milisegundos

Kishan Solanki
fuente
1

Bueno, podrías probar algo como esto:

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

convertir milisegundos a un valor de tiempo

anónimo
fuente