Pregunta

Me gustaría formatear una duración en segundos usando un patrón como H: MM: SS. Las utilidades actuales en Java están diseñadas para formatear una hora pero no una duración.

¿Fue útil?

Solución

Si usa una versión de Java anterior a 8 ... puede usar Joda Time y PeriodFormatter . Si realmente tiene una duración (es decir, una cantidad de tiempo transcurrida, sin referencia a un sistema de calendario), entonces probablemente debería usar Duration en su mayor parte; luego puede llamar a toPeriod (especificando cualquier PeriodType que desee reflejar si 25 horas se convierten en 1 día y 1 hora o no, etc.) para obtener un Period que puede formatear.

Si está utilizando Java 8 o posterior: normalmente sugeriría usar java.time.Duration para representar la duración. Luego puede llamar a getSeconds () o similar para obtener un número entero para el formato de cadena estándar según la respuesta de bobince si es necesario, aunque debe tener cuidado con la situación en la que la duración es negativa, ya que probablemente desee un signo negativo único en la cadena de salida. Entonces algo como:

public static String formatDuration(Duration duration) {
    long seconds = duration.getSeconds();
    long absSeconds = Math.abs(seconds);
    String positive = String.format(
        "%d:%02d:%02d",
        absSeconds / 3600,
        (absSeconds % 3600) / 60,
        absSeconds % 60);
    return seconds < 0 ? "-" + positive : positive;
}

Formatear de esta manera es razonablemente simple, aunque molestamente manual. Para análisis se convierte en un asunto más difícil en general ... Todavía podría usar Joda Time incluso con Java 8 si lo desea, por supuesto.

Otros consejos

Si no desea arrastrar en las bibliotecas, es lo suficientemente simple como hacerlo usando un Formateador o un atajo relacionado, por ejemplo. número entero de segundos dado s:

  String.format("%d:%02d:%02d", s / 3600, (s % 3600) / 60, (s % 60));

Uso Apache common's DurationFormatUtils así:

DurationFormatUtils.formatDuration(millis, "**H:mm:ss**", true);
long duration = 4 * 60 * 60 * 1000;
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault());
log.info("Duration: " + sdf.format(new Date(duration - TimeZone.getDefault().getRawOffset())));

Esto será más fácil con Java 9. Una Duration todavía no es formateable (lo que sé), pero se agregan métodos para obtener las horas, minutos y segundos, lo que hace que la tarea sea algo más sencillo:

        Duration diff = // ...;
        String hms = String.format("%d:%02d:%02d", 
                                   diff.toHoursPart(),
                                   diff.toMinutesPart(), 
                                   diff.toSecondsPart());

Esto puede ser un poco hacky, pero es una buena solución si uno está decidido a lograr esto usando Javacode java.time :

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.time.temporal.UnsupportedTemporalTypeException;

public class TemporalDuration implements TemporalAccessor {
    private static final Temporal BASE_TEMPORAL = LocalDateTime.of(0, 1, 1, 0, 0);

    private final Duration duration;
    private final Temporal temporal;

    public TemporalDuration(Duration duration) {
        this.duration = duration;
        this.temporal = duration.addTo(BASE_TEMPORAL);
    }

    @Override
    public boolean isSupported(TemporalField field) {
        if(!temporal.isSupported(field)) return false;
        long value = temporal.getLong(field)-BASE_TEMPORAL.getLong(field);
        return value!=0L;
    }

    @Override
    public long getLong(TemporalField field) {
        if(!isSupported(field)) throw new UnsupportedTemporalTypeException(new StringBuilder().append(field.toString()).toString());
        return temporal.getLong(field)-BASE_TEMPORAL.getLong(field);
    }

    public Duration getDuration() {
        return duration;
    }

    @Override
    public String toString() {
        return dtf.format(this);
    }

    private static final DateTimeFormatter dtf = new DateTimeFormatterBuilder()
            .optionalStart()//second
            .optionalStart()//minute
            .optionalStart()//hour
            .optionalStart()//day
            .optionalStart()//month
            .optionalStart()//year
            .appendValue(ChronoField.YEAR).appendLiteral(" Years ").optionalEnd()
            .appendValue(ChronoField.MONTH_OF_YEAR).appendLiteral(" Months ").optionalEnd()
            .appendValue(ChronoField.DAY_OF_MONTH).appendLiteral(" Days ").optionalEnd()
            .appendValue(ChronoField.HOUR_OF_DAY).appendLiteral(" Hours ").optionalEnd()
            .appendValue(ChronoField.MINUTE_OF_HOUR).appendLiteral(" Minutes ").optionalEnd()
            .appendValue(ChronoField.SECOND_OF_MINUTE).appendLiteral(" Seconds").optionalEnd()
            .toFormatter();

}

Esta será una de las nuevas características en java 7

JSR-310

Aquí hay una muestra más de cómo formatear la duración. Tenga en cuenta que esta muestra muestra la duración positiva y negativa como duración positiva.

import static java.time.temporal.ChronoUnit.DAYS;
import static java.time.temporal.ChronoUnit.HOURS;
import static java.time.temporal.ChronoUnit.MINUTES;
import static java.time.temporal.ChronoUnit.SECONDS;

import java.time.Duration;

public class DurationSample {
    public static void main(String[] args) {
        //Let's say duration of 2days 3hours 12minutes and 46seconds
        Duration d = Duration.ZERO.plus(2, DAYS).plus(3, HOURS).plus(12, MINUTES).plus(46, SECONDS);

        //in case of negative duration
        if(d.isNegative()) d = d.negated();

        //format DAYS HOURS MINUTES SECONDS 
        System.out.printf("Total duration is %sdays %shrs %smin %ssec.\n", d.toDays(), d.toHours() % 24, d.toMinutes() % 60, d.getSeconds() % 60);

        //or format HOURS MINUTES SECONDS 
        System.out.printf("Or total duration is %shrs %smin %sec.\n", d.toHours(), d.toMinutes() % 60, d.getSeconds() % 60);

        //or format MINUTES SECONDS 
        System.out.printf("Or total duration is %smin %ssec.\n", d.toMinutes(), d.getSeconds() % 60);

        //or format SECONDS only 
        System.out.printf("Or total duration is %ssec.\n", d.getSeconds());
    }
}

Esta es una opción de trabajo.

public static String showDuration(LocalTime otherTime){          
    DateTimeFormatter df = DateTimeFormatter.ISO_LOCAL_TIME;
    LocalTime now = LocalTime.now();
    System.out.println("now: " + now);
    System.out.println("otherTime: " + otherTime);
    System.out.println("otherTime: " + otherTime.format(df));

    Duration span = Duration.between(otherTime, now);
    LocalTime fTime = LocalTime.ofNanoOfDay(span.toNanos());
    String output = fTime.format(df);

    System.out.println(output);
    return output;
}

Llame al método con

System.out.println(showDuration(LocalTime.of(9, 30, 0, 0)));

Produce algo como:

otherTime: 09:30
otherTime: 09:30:00
11:31:27.463
11:31:27.463

¿Qué tal la siguiente función, que devuelve + H: MM: SS o + H: MM: SS.sss

public static String formatInterval(final long interval, boolean millisecs )
{
    final long hr = TimeUnit.MILLISECONDS.toHours(interval);
    final long min = TimeUnit.MILLISECONDS.toMinutes(interval) %60;
    final long sec = TimeUnit.MILLISECONDS.toSeconds(interval) %60;
    final long ms = TimeUnit.MILLISECONDS.toMillis(interval) %1000;
    if( millisecs ) {
        return String.format("%02d:%02d:%02d.%03d", hr, min, sec, ms);
    } else {
        return String.format("%02d:%02d:%02d", hr, min, sec );
    }
}
String duration(Temporal from, Temporal to) {
    final StringBuilder builder = new StringBuilder();
    for (ChronoUnit unit : new ChronoUnit[]{YEARS, MONTHS, WEEKS, DAYS, HOURS, MINUTES, SECONDS}) {
        long amount = unit.between(from, to);
        if (amount == 0) {
            continue;
        }
        builder.append(' ')
                .append(amount)
                .append(' ')
                .append(unit.name().toLowerCase());
        from = from.plus(amount, unit);
    }
    return builder.toString().trim();
}

Esta respuesta solo utiliza métodos Duration y funciona con Java 8:

public static String format(Duration d) {
    long days = d.toDays();
    d = d.minusDays(days);
    long hours = d.toHours();
    d = d.minusHours(hours);
    long minutes = d.toMinutes();
    d = d.minusMinutes(minutes);
    long seconds = d.getSeconds() ;
    return 
            (days ==  0?"":days+" jours,")+ 
            (hours == 0?"":hours+" heures,")+ 
            (minutes ==  0?"":minutes+" minutes,")+ 
            (seconds == 0?"":seconds+" secondes,");
}

Hay un enfoque elegante bastante simple (IMO), al menos para duraciones de menos de 24 horas:

DateTimeFormatter.ISO_LOCAL_TIME.format (value.addTo (LocalTime.of (0, 0)))

Los formateadores necesitan un objeto temporal para formatear, por lo que puede crear uno agregando la duración a una hora local de 00:00 (es decir, medianoche). Esto le dará un LocalTime que representa la duración desde la medianoche hasta ese momento, que luego es fácil de formatear en notación estándar HH: mm: ss. Esto tiene la ventaja de no necesitar una biblioteca externa, y utiliza la biblioteca java.time para hacer el cálculo, en lugar de calcular manualmente las horas, minutos y segundos.

Mi biblioteca Time4J ofrece una solución basada en patrones (similar a Apache DurationFormatUtils , pero más flexible):

Duration<ClockUnit> duration =
    Duration.of(-573421, ClockUnit.SECONDS) // input in seconds only
    .with(Duration.STD_CLOCK_PERIOD); // performs normalization to h:mm:ss-structure
String fs = Duration.formatter(ClockUnit.class, "+##h:mm:ss").format(duration);
System.out.println(fs); // output => -159:17:01

Este código demuestra las capacidades para manejar el desbordamiento de horas y el manejo de signos, consulte también la API de duración-formateador basado en el patrón .

En Scala, construyendo sobre la solución de YourBestBet pero simplificado:

def prettyDuration(seconds: Long): List[String] = seconds match {
  case t if t < 60      => List(s"${t} seconds")
  case t if t < 3600    => s"${t / 60} minutes" :: prettyDuration(t % 60)
  case t if t < 3600*24 => s"${t / 3600} hours" :: prettyDuration(t % 3600)
  case t                => s"${t / (3600*24)} days" :: prettyDuration(t % (3600*24))
}

val dur = prettyDuration(12345).mkString(", ") // => 3 hours, 25 minutes, 45 seconds

en scala, no se necesita biblioteca:

def prettyDuration(str:List[String],seconds:Long):List[String]={
  seconds match {
    case t if t < 60 => str:::List(s"${t} seconds")
    case t if (t >= 60 && t< 3600 ) => List(s"${t / 60} minutes"):::prettyDuration(str, t%60)
    case t if (t >= 3600 && t< 3600*24 ) => List(s"${t / 3600} hours"):::prettyDuration(str, t%3600)
    case t if (t>= 3600*24 ) => List(s"${t / (3600*24)} days"):::prettyDuration(str, t%(3600*24))
  }
}
val dur = prettyDuration(List.empty[String], 12345).mkString("")

No vi este, así que pensé en agregarlo:

Date started=new Date();
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
task
long duration=new Date().getTime()-started.getTime();
System.out.println(format.format(new Date(duration));

Solo funciona durante 24 horas, pero eso es lo que generalmente quiero para la duración.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top