Comment formater une durée en java? (par exemple format H: MM: SS)


165

Je voudrais formater une durée en secondes en utilisant un modèle comme H: MM: SS. Les utilitaires actuels en java sont conçus pour formater une heure mais pas une durée.

Réponses:


84

Si vous utilisez une version de Java antérieure à 8 ... vous pouvez utiliser Joda Time et PeriodFormatter. Si vous avez vraiment une durée (c'est-à-dire une durée écoulée, sans référence à un système de calendrier), vous devriez probablement l'utiliser Durationpour la plupart - vous pouvez alors appeler toPeriod(en spécifiant ce que PeriodTypevous voulez pour indiquer si 25 heures deviennent 1 jour et 1 heure ou pas, etc.) pour obtenir un Periodformat que vous pouvez formater.

Si vous utilisez Java 8 ou version ultérieure: je suggère normalement d'utiliser java.time.Durationpour représenter la durée. Vous pouvez ensuite appeler getSeconds()ou similaire pour obtenir un entier pour le formatage de chaîne standard selon la réponse de bobince si vous en avez besoin - bien que vous devriez faire attention à la situation où la durée est négative, car vous voulez probablement un seul signe négatif dans la chaîne de sortie . Donc quelque chose comme:

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

Le formatage de cette manière est raisonnablement simple, bien que manuel ennuyeux. Pour l' analyse cela devient une question plus difficile en général ... Vous pouvez toujours utiliser Joda Time même avec Java 8 si vous le souhaitez, bien sûr.


Recommanderiez-vous toujours d'utiliser la bibliothèque Joda Time lors de l'utilisation de Java 8?
Tim Büthe

1
@ TimBüthe: Non, je commencerais à utiliser java.time dans ce cas. (Bien que je ne puisse pas trouver immédiatement un équivalent de PeriodFormatter ...)
Jon Skeet

1
PeriodFormatter n'est pas inclus. C'est l'une des différences entre l'API Java 8 Date Time et Joda Time.
Tim Büthe

1
@RexKerr: Eh bien, il y a toujours "utiliser Joda Time" si vous le souhaitez, bien sûr. Éditera à nouveau. (Avec le recul, j'aurais dû recommander Duration de toute façon, d'après les sons. Une modification importante est requise ...)
Jon Skeet

4
@MarkJeronimus serait encore plus facile à utiliser les Durationméthodes de s: duration.toHours(), duration.toMinutesPart()et duration.toSecondsPart()qui sont disponibles depuis Java 9. Et pour obtenir la valeur absolue peut utiliser duration.abs().
recke96

195

Si vous ne voulez pas faire glisser dans les bibliothèques, c'est assez simple de le faire vous-même en utilisant un formateur ou un raccourci associé, par exemple. donné nombre entier de secondes s:

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

4
N'a pas besoin d'être un entier. Si vous avez deux dates, branchez simplement date1.getTime () - date2.getTime () dans l'équation ci-dessus qui utilise la longue primitive et fonctionne toujours.
Josh

Et si le décalage horaire est supérieur à 24h?
Rajish

2
@Rajish: il faudrait demander au PO ce qu'il voulait dans ce cas! Bien sûr, vous pouvez le séparer en s/86400, (s%86400)/3600...jours si nécessaire ...
bobince

Ma solution à s > 86400 (un jour): "\u221E"- infini
QED

Si le décalage horaire est supérieur à 24 heures, il formate toujours bien la durée en heures, minutes et secondes. Pour mon cas d'utilisation, c'est comme prévu.
Audrius Meskauskas

123

J'utilise DurationFormatUtils d' Apache commun comme ceci:

DurationFormatUtils.formatDuration(millis, "**H:mm:ss**", true);

13
La manière la plus simple possible (dans le style apache commons). Vous avez même la méthode formatDurationHMS que vous utiliserez probablement la plupart du temps.
Marko

Parfait! Vous pouvez également ajouter un autre texte dans l'apostrophe: DurationFormatUtils.formatDuration (durationInMillis, "m 'minutes', s 'seconds'")
mihca

29

C'est plus facile depuis Java 9. A Durationn'est toujours pas formatable, mais des méthodes pour obtenir les heures, minutes et secondes sont ajoutées, ce qui rend la tâche un peu plus simple:

    LocalDateTime start = LocalDateTime.of(2019, Month.JANUARY, 17, 15, 24, 12);
    LocalDateTime end = LocalDateTime.of(2019, Month.JANUARY, 18, 15, 43, 33);
    Duration diff = Duration.between(start, end);
    String hms = String.format("%d:%02d:%02d", 
                                diff.toHours(), 
                                diff.toMinutesPart(), 
                                diff.toSecondsPart());
    System.out.println(hms);

Le résultat de cet extrait de code est:

24:19:21


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

14
Hah! Frappez simplement le mur avec la normalisation du fuseau horaire lorsque vous utilisez cette méthode. Vous devez ajouter sdf.setTimeZone(TimeZone.getTimeZone("GMT+0"));avant d'utiliser la formatfonction.
Rajish

Il est recommandé d'utiliser DateFormat.getDateInstance()au lieu de classes concrètes.
Abhijit Sarkar il y a

7

Cela peut être un peu piraté, mais c'est une bonne solution si l'on veut accomplir cela en utilisant Java 8 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();

}

J'ai juste essayé ceci, et j'ai remarqué que si j'appelais format pour "hh: mm: ss" alors que je n'avais pas d'heures ou de minutes pour formater (par exemple Duration.ofSeconds(20)), alors j'obtiendrais un UnsupportedTemporalTypeException). J'ai simplement supprimé le code vérifiant si la différence est == 01. J'ai pensé qu'il y 01avait une sorte de valeur de masquage que je ne comprends pas complètement, pouvez-vous l'expliquer?
Groostav

Cela a en fait très bien fonctionné. Sur mon cas, j'ai même ajouté des millisecondes ... Concernant la isSupportedméthode, il renvoie les informations s'il existe un champ valide à afficher sur la chaîne lisible par l'homme. Quoi qu'il en soit, le "masquage" n'est en fait pas un masque. Le code return value!=0lne montre pas return value!=01. Veuillez utiliser le copier-coller la prochaine fois.
MrJames

1
L'interface TemporalAccessorn'est pas censée être mal utilisée pendant des durées. JSR-310 a conçu l'interface TemporalAmountà cet effet.
Meno Hochschild le

1
Je vous recommande de toujours utiliser des majuscules Lpour signifier une longvaleur. Il est si facile de mal interpréter les minuscules lpour le chiffre 1, comme je viens de le démontrer.
Ole VV

6

Voici un autre exemple de formatage de la durée. Notez que cet échantillon montre à la fois la durée positive et la durée négative comme une durée positive.

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

5

Cette réponse utilise uniquement des Durationméthodes et fonctionne avec 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,");
}

4

Ceci est une option de travail.

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

Appelez la méthode avec

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

Produit quelque chose comme:

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

2
Cela échouera pour des durées supérieures à 23: 59: 59.999.
Michel Jung

4

Que diriez-vous de la fonction suivante, qui renvoie soit + H: MM: SS ou + 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 );
    }
}

4

Il existe une approche assez simple et élégante (IMO), au moins pour des durées inférieures à 24 heures:

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

Les formateurs ont besoin d'un objet temporel pour formater, vous pouvez donc en créer un en ajoutant la durée à un LocalTime de 00:00 (c'est-à-dire minuit). Cela vous donnera un LocalTime représentant la durée de minuit à cette heure, qui est alors facile à formater en notation standard HH: mm: ss. Cela a l'avantage de ne pas avoir besoin d'une bibliothèque externe et utilise la bibliothèque java.time pour faire le calcul, plutôt que de calculer manuellement les heures, les minutes et les secondes.


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

1

en utilisant cette fonction

private static String strDuration(long duration) {
    int ms, s, m, h, d;
    double dec;
    double time = duration * 1.0;

    time = (time / 1000.0);
    dec = time % 1;
    time = time - dec;
    ms = (int)(dec * 1000);

    time = (time / 60.0);
    dec = time % 1;
    time = time - dec;
    s = (int)(dec * 60);

    time = (time / 60.0);
    dec = time % 1;
    time = time - dec;
    m = (int)(dec * 60);

    time = (time / 24.0);
    dec = time % 1;
    time = time - dec;
    h = (int)(dec * 24);
    
    d = (int)time;
    
    return (String.format("%d d - %02d:%02d:%02d.%03d", d, h, m, s, ms));
}

Allez sur github.com/ipserc/duration pour obtenir une version packagée de cette fonction
ipserc

C'était le code que je cherchais, la réponse acceptée est excellente, mais il manque les jours dont j'avais besoin
Filnor il y a

0

Ma bibliothèque Time4J propose une solution basée sur des modèles (similaire Apache DurationFormatUtils, mais plus 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

Ce code montre les capacités de gérer le dépassement d'heures et la gestion des signes, voir aussi l'API de duration-formatter basé sur un modèle .


0

Dans scala (j'ai vu d'autres tentatives et je n'ai pas été impressionné):

def formatDuration(duration: Duration): String = {
  import duration._ // get access to all the members ;)
  f"$toDaysPart $toHoursPart%02d:$toMinutesPart%02d:$toSecondsPart%02d:$toMillisPart%03d"
}

Ça a l'air horrible, oui? Eh bien, c'est pourquoi nous utilisons des IDE pour écrire ce truc de sorte que les appels de méthode ( $toHoursPartetc.) soient d'une couleur différente.

L' interpolateur de chaîne f"..."est un printf/ String.formatstyle (ce qui permet à l' $injection de code de fonctionner) Étant donné une sortie de 1 14:06:32.583, la fchaîne interpolée serait équivalente àString.format("1 %02d:%02d:%02d.%03d", 14, 6, 32, 583)


-1

Dans Scala, s'appuyer sur la solution de YourBestBet mais simplifié:

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

-2

dans scala, aucune bibliothèque nécessaire:

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("")

3
Ce n'est pas une bonne publicité pour Scala? Aucune bibliothèque nécessaire, mais autant de code ...?
Adam

J'aime l'approche récursive, mais elle peut être beaucoup simplifiée: stackoverflow.com/a/52992235/3594403
dpoetzsch

-2

Je n'ai pas vu celui-ci alors j'ai pensé l'ajouter:

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

Cela ne fonctionne que pendant 24 heures, mais c'est ce que je veux généralement pour la durée.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.