Как отформатировать продолжительность в java? (например, формат H: MM: SS)

Я хотел бы отформатировать продолжительность в секундах, используя такой шаблон, как H: MM: SS. Текущие утилиты в java предназначены для форматирования времени, но не продолжительности.


person Community    schedule 05.11.2008    source источник


Ответы (20)


Если вы используете версию Java до 8 ... вы можете использовать Joda Time и _ 1_. Если у вас действительно есть продолжительность (т.е. истекшее количество времени, без ссылки на календарную систему), вам, вероятно, следует использовать Duration по большей части - затем вы можете вызвать toPeriod (указав то PeriodType, которое вы хотите отразить, 25 часов превращаются в 1 день и 1 час или нет и т. Д.), Чтобы получить Period, который вы можете отформатировать.

Если вы используете Java 8 или новее: обычно я предлагаю использовать java.time.Duration для обозначения продолжительности. Затем вы можете вызвать getSeconds() или тому подобное, чтобы получить целое число для стандартного форматирования строки в соответствии с ответом bobince, если вам нужно - хотя вы должны быть осторожны с ситуацией, когда продолжительность отрицательная, поскольку вам, вероятно, понадобится single отрицательный знак в выходной строке. Так что-то вроде:

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

Форматирование таким способом разумно просто, хотя и раздражает вручную. Для синтаксического анализа в целом становится сложнее ... Вы, конечно, можете использовать Joda Time даже с Java 8, если хотите.

person Jon Skeet    schedule 05.11.2008
comment
Вы бы по-прежнему рекомендовали использовать библиотеку Joda Time при использовании Java 8? - person Tim Büthe; 04.08.2014
comment
@ TimBüthe: Нет, в этом случае я бы начал использовать java.time. (Хотя я не могу сразу найти аналог PeriodFormatter ...) - person Jon Skeet; 04.08.2014
comment
PeriodFormatter не включен. Это одно из различий между Java 8 Date Time API и Joda Time. - person Tim Büthe; 04.08.2014
comment
@ TimBüthe: Верно. Я бы хотя бы подумал о том, чтобы написать свой собственный PeriodFormatter - или, возможно, включить Joda Time только для этого, в зависимости от контекста. - person Jon Skeet; 04.08.2014
comment
Так вот где мы сейчас находимся? java.time замечательно, если вы не хотите форматировать длительности (действительно ли такая непонятная функция?), И в каком случае вам нужно иметь кучу непонятных применений joda-time? Я действительно надеюсь, что эта проблема будет решена до появления Java-9. - person Groostav; 13.04.2015
comment
@Groostav: Я думаю, что форматирование продолжительности относительно редко. Альтернативой использованию текстового формата периода может быть просто передача количества наносекунд в виде числа. Но да, было бы хорошо увидеть это в Java 9 :) - person Jon Skeet; 13.04.2015
comment
Этот ответ неадекватен для Java 8 (подозреваю, что где-то есть что-то), которая теперь является стандартной. Таким образом, ответ устарел. - person Rex Kerr; 28.10.2015
comment
@RexKerr: Дело не в том, что оно устарело - просто, насколько я понимаю, в Java 8 не предусмотрено настраиваемое форматирование периода. Насколько я могу судить, с течением времени этот факт не изменился. Если вы знаете о способе форматирования точки индивидуальным образом в Java 8, просьба предоставить эту информацию. - person Jon Skeet; 28.10.2015
comment
Я тоже не знаю, как это сделать, и подозреваю, что его не существует (за исключением использования LocalTime для периодов до 24 часов, что не идеально). Но ни вы не можете, ни я не знаю, ни подозреваю, что есть что-то, что действительно является хорошим ответом на вопрос, как мне это сделать, не так ли? Если кто-то другой думает: «О, я бы хотел отформатировать продолжительность как Ч: ММ: СС!», И они читают ваш ответ, они говорят: «Хорошо, теперь у меня точка и ... Я застрял? ! - person Rex Kerr; 29.10.2015
comment
@RexKerr: Ну, конечно, можно использовать Joda Time, если хотите. Буду редактировать снова. (Оглядываясь назад, я должен был порекомендовать Duration, судя по всему. Требуются значительные изменения ...) - person Jon Skeet; 29.10.2015
comment
Легче использовать новые методы Math.floorMod и Math.floorDiv вместо ручной отрицательной проверки. - person Mark Jeronimus; 08.05.2018
comment
@MarkJeronimus: Я предлагаю вам добавить новый ответ, так как я не сразу понимаю, как это упростит задачу. - person Jon Skeet; 09.05.2018
comment
Моя ошибка. Я думал, что обнаружил что-то случайно, когда ошибка в моей программе привела к отрицательной продолжительности, а затем к рендерингу. Но в конце концов он оказался неверным. - person Mark Jeronimus; 09.05.2018
comment
@MarkJeronimus Еще проще было бы использовать методы Durations: duration.toHours(), duration.toMinutesPart() и duration.toSecondsPart(), которые доступны начиная с Java 9. А для получения абсолютного значения можно использовать duration.abs(). - person recke96; 15.06.2018
comment
Ответ @ recke96 отличный, если у вас есть программа проверки кода, которая учитывает магические числа 60/3600 - person Laurens; 18.12.2019

Если вы не хотите перетаскивать библиотеки, достаточно просто сделать это самостоятельно, используя Formatter или связанный ярлык, например. заданное целое число секунд s:

  String.format("%d:%02d:%02d", s / 3600, (s % 3600) / 60, (s % 60));
person bobince    schedule 05.11.2008
comment
Не обязательно должно быть целым. Если у вас две даты, просто вставьте date1.getTime () - date2.getTime () в приведенное выше уравнение, которое использует примитив long и все еще работает. - person Josh; 06.11.2008
comment
Что делать, если разница во времени превышает 24 часа? - person Rajish; 15.07.2011
comment
@Rajish: тебе нужно спросить ОП, что они хотят в таком случае! Конечно, при необходимости вы можете разделить это на s/86400, (s%86400)/3600... на несколько дней ... - person bobince; 16.07.2011
comment
Мое решение s > 86400 (один день): "\u221E" - бесконечность - person QED; 24.08.2012
comment
Если разница во времени превышает 24 часа, он по-прежнему хорошо форматирует продолжительность в часах, минутах и ​​секундах. Для моего случая использования это ожидаемо. - person Audrius Meskauskas; 29.08.2015

Я использую Apache common DurationFormatUtils вот так:

DurationFormatUtils.formatDuration(millis, "**H:mm:ss**", true);
person Gili Nachum    schedule 05.09.2013
comment
Самый простой способ (в стиле apache commons). У вас даже есть метод formatDurationHMS, который вы, вероятно, будете использовать большую часть времени. - person Marko; 29.03.2015
comment
Идеально! Вы также можете добавить другой текст внутри апострофа: DurationFormatUtils.formatDuration (durationInMillis, m 'минуты', s 'секунды') - person mihca; 18.02.2020

Это стало проще, начиная с Java 9. A Duration по-прежнему нельзя форматировать, но добавлены методы для получения часов, минут и секунд, что несколько упрощает задачу:

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

Результат этого фрагмента:

24:19:21

person Ole V.V.    schedule 03.06.2017

Существует довольно простой и (IMO) элегантный подход, по крайней мере, для продолжительности менее 24 часов:

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

Форматировщикам требуется временный объект для форматирования, поэтому вы можете создать его, добавив длительность к LocalTime, равному 00:00 (т. Е. Полночь). Это даст вам LocalTime, представляющий продолжительность от полуночи до этого времени, который затем легко отформатировать в стандартной нотации ЧЧ: мм: сс. Это имеет то преимущество, что не требует внешней библиотеки и использует библиотеку java.time для расчета, а не вручную вычисляет часы, минуты и секунды.

person ctg    schedule 20.02.2019
comment
хороший трюк. Для периода более 24 часов это дает вам оставшуюся модификацию 24 часа, что не совсем то, что мне нужно, но я согласен, что это самый элегантный вариант. Тем не менее, я довольно разочарован тем, что объект Duration в java 8 не имеет встроенных инструментов форматирования. - person Groostav; 26.06.2021

Этот ответ использует только Duration методы и работает с 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,");
}
person lauhub    schedule 28.06.2018

Это может показаться хакерским, но это хорошее решение, если кто-то хочет добиться этого с помощью java.time в Java 8:

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

}
person patrick    schedule 25.01.2015
comment
Я просто попробовал это и заметил, что если бы я вызвал формат для hh: mm: ss, когда у меня не было часов или минут для форматирования (например, Duration.ofSeconds(20)), я бы получил UnsupportedTemporalTypeException). Я просто удалил код, проверяя, есть ли разница == 01. Я подумал, что 01 было своего рода маскировкой, которую я не совсем понимаю, вы можете это объяснить? - person Groostav; 13.04.2015
comment
Это действительно сработало очень хорошо. В моем случае я даже добавил миллисекунды ... Что касается метода isSupported, он возвращает информацию, если есть допустимое поле для отображения в читаемой человеком строке. В любом случае маскировка на самом деле не маска. Код показывает return value!=0l, а не return value!=01. Пожалуйста, в следующий раз воспользуйтесь копипастом. - person MrJames; 08.11.2016
comment
Интерфейс TemporalAccessor не должен использоваться ненадлежащим образом в течение длительного времени. JSR-310 разработал интерфейс TemporalAmount для этой цели. - person Meno Hochschild; 04.01.2017
comment
Я рекомендую всегда использовать L в верхнем регистре для обозначения long значения. Как только что было продемонстрировано, очень легко неправильно интерпретировать строчные l вместо цифры 1. - person Ole V.V.; 06.05.2018

Вот еще один пример того, как отформатировать продолжительность. Обратите внимание, что этот образец показывает как положительную, так и отрицательную продолжительность как положительную.

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());
    }
}
person Pavel_H    schedule 06.09.2016

Как насчет следующей функции, которая возвращает либо + H: MM: SS, либо + 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 );
    }
}
person mksteve    schedule 28.06.2017

Это рабочий вариант.

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

Вызовите метод с помощью

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

Производит что-то вроде:

otherTime: 09:30
otherTime: 09:30:00
11:31:27.463
11:31:27.463
person sbclint    schedule 01.04.2014
comment
Это не удастся для продолжительности более 23: 59: 59.999. - person Michel Jung; 11.03.2017

Я не уверен, что вы хотите, но посмотрите этот вспомогательный класс Android

import android.text.format.DateUtils

Например: DateUtils.formatElapsedTime()

android date duration прошедшее время

person Sergei Maleev    schedule 02.09.2020

Вы можете использовать java.time.Duration, смоделированный на стандарты ISO-8601 и был представлен вместе с Java-8 как часть реализации JSR-310. В Java-9 было введено еще несколько удобных методов.

Демо:

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.Month;

public class Main {
    public static void main(String[] args) {
        LocalDateTime startDateTime = LocalDateTime.of(2020, Month.DECEMBER, 10, 15, 20, 25);
        LocalDateTime endDateTime = LocalDateTime.of(2020, Month.DECEMBER, 10, 18, 24, 30);

        Duration duration = Duration.between(startDateTime, endDateTime);
        // Default format
        System.out.println(duration);

        // Custom format
        // ####################################Java-8####################################
        String formattedElapsedTime = String.format("%02d:%02d:%02d", duration.toHours() % 24,
                duration.toMinutes() % 60, duration.toSeconds() % 60);
        System.out.println(formattedElapsedTime);
        // ##############################################################################

        // ####################################Java-9####################################
        formattedElapsedTime = String.format("%02d:%02d:%02d", duration.toHoursPart(), duration.toMinutesPart(),
                duration.toSecondsPart());
        System.out.println(formattedElapsedTime);
        // ##############################################################################
    }
}

Вывод:

PT3H4M5S
03:04:05
03:04:05

Узнайте о современном API даты и времени на странице Trail: Date Time .

person Arvind Kumar Avinash    schedule 30.12.2020

Есть еще один способ сделать это для java8

public String formatDuration(Duration duration) {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("h:mm.SSS");
    return LocalTime.ofNanoOfDay(duration.toNanos()).format(formatter);
}
person stanley    schedule 05.01.2021
comment
Это прекрасно работает, но что это за магия? LOL Почему и как это на самом деле работает? ofNanoOfDay, _2 _... и почему LocalTime. В контексте продолжительности это не имеет смысла: - \, но все же помогает. - person t3chb0t; 28.01.2021
comment
Первое голосование! Вы сделали меня счастливым сегодня :) Duration нужно преобразовать в LocalTime, чтобы использовать свой format метод. Что касается нано, они использовались для получения количества миллисекунд, но мы также можем написать что-то вроде LocalTime.ofSecondOfDay(duration.getSeconds()).format(formatter) - person stanley; 31.01.2021
comment
Прохладный! Секунданты кажутся более естественными: -] Да, либо это большая работа, чтобы заработать репутацию здесь, либо вам, должно быть, очень повезло получить сотни голосов за вопрос или ответ. Люди довольно неохотно идут на голосование. Иногда просто необходимо больше времени, чтобы кто-то заметил ваш ответ. Удачи и еще раз спасибо за это короткое решение. Остальные настолько изощрены :) - person t3chb0t; 31.01.2021

используя эту функцию

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));
}
person ipserc    schedule 02.07.2020
comment
Перейдите на страницу github.com/ipserc/duration, чтобы получить упакованную версию этой функции. - person ipserc; 05.07.2020
comment
Это был код, который я искал, принятый ответ отличный, но ему не хватает дней, которые мне нужны. - person Filnor; 22.07.2020

Моя библиотека Time4J предлагает решение на основе шаблонов (аналогично Apache DurationFormatUtils, но более гибкое):

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

Этот код демонстрирует возможности обработки переполнения часов и обработки знаков, см. Также API длительности форматирования на основе шаблона.

person Meno Hochschild    schedule 14.11.2016

В scala (видел и другие попытки, и не впечатлился):

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

Выглядит ужасно, да? Вот почему мы используем IDE для написания этого материала, чтобы вызовы методов ($toHoursPart и т. Д.) Были другого цвета.

f"..." - это строковый интерполятор в стиле _4 _ / _ 5_ (что позволяет использовать инъекцию кода $). При выходе 1 14:06:32.583 интерполированная строка f будет эквивалентна String.format("1 %02d:%02d:%02d.%03d", 14, 6, 32, 583)

person Stephen    schedule 19.06.2020

Глядя на все эти вычисления, может быть полезно, что большинство единиц (часы, минуты и т. Д.) Имеют .toFooPart() удобный метод.

E.g.

Duration.ofMinutes(110L).toMinutesPart() == 50

Чтение: количество минут, прошедших до следующего значения родительской единицы (час).

person sschrass    schedule 05.02.2021

В Scala на основе решения YourBestBet, но в упрощенном виде:

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
person dpoetzsch    schedule 25.10.2018

в scala библиотека не нужна:

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("")
person YourBestBet    schedule 03.04.2018
comment
Это не лучшая реклама Scala? Библиотеки не требуется, зато столько же кода ...? - person Adam; 23.04.2018
comment
Мне нравится рекурсивный подход, но его можно значительно упростить: stackoverflow.com/a/52992235/3594403 - person dpoetzsch; 25.10.2018

person    schedule
comment
Ха! Просто поразите стену с нормализацией часового пояса при использовании этого метода. Вы должны добавить sdf.setTimeZone(TimeZone.getTimeZone("GMT+0")); перед использованием функции format. - person Rajish; 15.07.2011
comment
Вместо конкретных классов рекомендуется использовать DateFormat.getDateInstance(). - person Abhijit Sarkar; 25.07.2020
comment
Это решение хрупкое. Он перейдет на 00:00:00.000, когда количество часов достигнет 24 часов. Кроме того, отрицательная одна секунда форматируется как 23:59:59.000. - person V.S.; 12.08.2020