Wie formatiere ich eine Dauer in Java? (zB Format H: MM: SS)

164

Ich möchte eine Dauer in Sekunden mit einem Muster wie H: MM: SS formatieren. Die aktuellen Dienstprogramme in Java sind so konzipiert, dass sie eine Zeit, aber keine Dauer formatieren.

naXa
quelle

Antworten:

83

Wenn Sie eine Version von Java vor 8 verwenden, können Sie Joda Time und verwenden PeriodFormatter. Wenn Sie wirklich eine Dauer haben (dh eine verstrichene Zeitspanne ohne Bezug auf ein Kalendersystem), sollten Sie diese wahrscheinlich Durationgrößtenteils verwenden - Sie können dann anrufen toPeriod(indem PeriodTypeSie angeben, was auch immer Sie reflektieren möchten, ob 25 Stunden werden) 1 Tag und 1 Stunde oder nicht usw.), um eine zu erhalten, Perioddie Sie formatieren können.

Wenn Sie Java 8 oder höher verwenden: Ich würde normalerweise empfehlen java.time.Duration, die Dauer zu verwenden. Sie können dann aufrufen getSeconds()oder ähnliches, um bei Bedarf eine Ganzzahl für die Standardzeichenfolgenformatierung gemäß der Antwort von bobince zu erhalten. Sie sollten jedoch auf die Situation achten, in der die Dauer negativ ist, da Sie wahrscheinlich ein einzelnes negatives Vorzeichen in der Ausgabezeichenfolge wünschen . Also so etwas wie:

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

Die Formatierung auf diese Weise ist relativ einfach, wenn auch ärgerlich manuell. Für das Parsen wird es im Allgemeinen schwieriger ... Sie können Joda Time auch mit Java 8 verwenden, wenn Sie möchten.

Jon Skeet
quelle
Würden Sie trotzdem empfehlen, die Joda Time-Bibliothek zu verwenden, wenn Sie Java 8 verwenden?
Tim Büthe
1
@ TimBüthe: Nein, in diesem Fall würde ich java.time verwenden. (Obwohl ich nicht sofort ein Äquivalent von PeriodFormatter finden kann ...)
Jon Skeet
1
PeriodFormatter ist nicht enthalten. Das ist einer der Unterschiede zwischen Java 8 Date Time API und Joda Time.
Tim Büthe
1
@ RexKerr: Nun, es gibt natürlich immer noch "benutze Joda Time", wenn du willst. Wird erneut bearbeitet. (Rückblickend hätte ich Duration nach den Geräuschen sowieso empfehlen sollen. Erhebliche Bearbeitung erforderlich ...)
Jon Skeet
4
@MarkJeronimus Noch einfacher wäre es , die verwenden Durations Methoden: duration.toHours(), duration.toMinutesPart()und duration.toSecondsPart()die seit Java verfügbar ist 9. Und der absolute Wert man verwenden kann zu bekommen duration.abs().
recke96
195

Wenn Sie keine Bibliotheken ziehen möchten, können Sie einfach einen Formatierer oder eine zugehörige Verknüpfung verwenden, z. gegebene ganzzahlige Anzahl von Sekunden s:

  String.format("%d:%02d:%02d", s / 3600, (s % 3600) / 60, (s % 60));
Bobince
quelle
4
Muss nicht ganzzahlig sein. Wenn Sie zwei Datumsangaben haben, fügen Sie einfach date1.getTime () - date2.getTime () in die obige Gleichung ein, die das lange Grundelement verwendet und weiterhin funktioniert.
Josh
Was ist, wenn der Zeitunterschied länger als 24 Stunden ist?
Rajish
2
@ Rajish: Du müsstest das OP fragen, was sie in diesem Fall wollten! Natürlich können Sie es s/86400, (s%86400)/3600...bei Bedarf
tagelang aufteilen
Meine Lösung auf s > 86400 (eines Tages): "\u221E"- unendlich
QED
Wenn der Zeitunterschied mehr als 24 Stunden beträgt, wird die Dauer immer noch gut in Stunden, Minuten und Sekunden formatiert. Für meinen Anwendungsfall ist dies wie erwartet.
Audrius Meskauskas
122

Ich benutze die DurationFormatUtils von Apache common wie folgt:

DurationFormatUtils.formatDuration(millis, "**H:mm:ss**", true);
Gili Nachum
quelle
13
Der einfachste Weg (im Apache-Commons-Stil). Sie haben sogar die formatDurationHMS-Methode, die Sie wahrscheinlich die meiste Zeit verwenden werden.
Marko
Perfekt! Sie können auch anderen Text innerhalb des Apostrophs hinzufügen: DurationFormatUtils.formatDuration (durationInMillis, "m 'Minuten', s 'Sekunden'")
mihca
29

Dies ist einfacher, da Java 9. A Durationimmer noch nicht formatierbar ist, aber Methoden zum Abrufen der Stunden, Minuten und Sekunden hinzugefügt werden, was die Aufgabe etwas einfacher macht:

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

Die Ausgabe dieses Snippets lautet:

24:19:21

Ole VV
quelle
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())));
Mihai Vasilache
quelle
14
Hah! Schlagen Sie bei dieser Methode einfach mit Zeitzonennormalisierung gegen die Wand. Sie müssen hinzufügen, sdf.setTimeZone(TimeZone.getTimeZone("GMT+0"));bevor Sie die formatFunktion verwenden.
Rajish
7

Dies mag etwas hackig sein, aber es ist eine gute Lösung, wenn man dies mit Java 8 erreichen möchte 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();

}
Patrick
quelle
Ich das gerade versucht, und ich bemerkte , dass , wenn ich Format für „hh: mm: ss“ genannt , als ich knapp über keine Stunden oder Minuten - Format (zB Duration.ofSeconds(20)), dann würde ich eine bekommen UnsupportedTemporalTypeException). Ich habe einfach den Code entfernt und überprüft, ob der Unterschied besteht == 01. Ich dachte, 01es wäre eine Art Maskierungswert, den ich nicht vollständig verstehe. Können Sie das erklären?
Groostav
Das hat eigentlich sehr gut funktioniert. In meinem Fall habe ich sogar Millisekunden hinzugefügt ... In Bezug auf die isSupportedMethode werden die Informationen zurückgegeben, wenn in der vom Menschen lesbaren Zeichenfolge ein gültiges Feld angezeigt werden soll. Jedenfalls ist die "Maskierung" eigentlich keine Maske. Der Code zeigt return value!=0lnicht return value!=01. Bitte verwenden Sie beim nächsten Mal Copy-Paste.
MrJames
1
Die Schnittstelle TemporalAccessordarf nicht für längere Zeit missbraucht werden. JSR-310 hat die Schnittstelle TemporalAmountfür diesen Zweck entwickelt.
Meno Hochschild
1
Ich empfehle, immer Großbuchstaben Lzu verwenden, um einen longWert zu kennzeichnen . Es ist so leicht, Kleinbuchstaben lfür Ziffer 1 falsch zu lesen , wie gerade gezeigt wurde.
Ole VV
6

Hier ist ein weiteres Beispiel zum Formatieren der Dauer. Beachten Sie, dass dieses Beispiel sowohl die positive als auch die negative Dauer als positive Dauer anzeigt.

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

Diese Antwort verwendet nur DurationMethoden und funktioniert mit 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,");
}
Lauhub
quelle
4

Wie wäre es mit der folgenden Funktion, die entweder + H: MM: SS oder + H: MM: SS.sss zurückgibt

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 );
    }
}
mksteve
quelle
4

Es gibt einen ziemlich einfachen und (IMO) eleganten Ansatz, zumindest für eine Dauer von weniger als 24 Stunden:

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

Formatierer benötigen ein zeitliches Objekt zum Formatieren, sodass Sie eines erstellen können, indem Sie die Dauer zu einer LocalTime von 00:00 (dh Mitternacht) hinzufügen. Auf diese Weise erhalten Sie eine LocalTime, die die Dauer von Mitternacht bis zu dieser Zeit darstellt und dann einfach in der Standardnotation HH: mm: ss formatiert werden kann. Dies hat den Vorteil, dass keine externe Bibliothek erforderlich ist, und verwendet die Bibliothek java.time, um die Berechnung durchzuführen, anstatt die Stunden, Minuten und Sekunden manuell zu berechnen.

ctg
quelle
3

Dies ist eine funktionierende Option.

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

Rufen Sie die Methode mit auf

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

Produziert so etwas wie:

otherTime: 09:30
otherTime: 09:30:00
11:31:27.463
11:31:27.463
sbclint
quelle
2
Dies schlägt bei einer Dauer von mehr als 23: 59: 59.999 fehl.
Michel Jung
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();
}
Bax
quelle
0

Meine Bibliothek Time4J bietet eine musterbasierte Lösung (ähnlich Apache DurationFormatUtils, aber flexibler):

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

Dieser Code demonstriert die Funktionen zum Behandeln von Stundenüberlauf und Zeichenbehandlung, siehe auch die API des auf Mustern basierenden Dauerformatierers .

Meno Hochschild
quelle
0

In Scala (ich habe einige andere Versuche gesehen und war nicht beeindruckt):

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

Sieht ja schrecklich aus? Deshalb verwenden wir IDEs, um dieses Zeug so zu schreiben, dass die Methode ($toHoursPart usw.) eine andere Farbe haben.

Dies f"..."ist ein printf/ String.formatstyle-String-Interpolator (wodurch die $Code-Injection funktioniert). Bei einer Ausgabe von wäre 1 14:06:32.583der finterpolierte String äquivalent zuString.format("1 %02d:%02d:%02d.%03d", 14, 6, 32, 583)

Stephen
quelle
0

mit dieser func

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));
}
ipserc
quelle
Unter github.com/ipserc/duration erhalten Sie eine
Paketversion
-1

In Scala auf der Lösung von YourBestBet aufbauen, aber vereinfacht:

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
dpoetzsch
quelle
-2

In Scala wird keine Bibliothek benötigt:

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("")
YourBestBet
quelle
3
Dies ist keine großartige Werbung für Scala? Keine Bibliothek benötigt, aber genauso viel Code ...?
Adam
Ich mag den rekursiven Ansatz, aber er kann sehr vereinfacht werden: stackoverflow.com/a/52992235/3594403
dpoetzsch
-2

Ich habe diesen nicht gesehen, also dachte ich, ich würde ihn hinzufügen:

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

Es funktioniert nur 24 Stunden, aber das ist es, was ich normalerweise für die Dauer möchte.

Ronald Wentworth
quelle