Як відформатувати тривалість у Java? (наприклад, формат H: MM: SS)


165

Я хотів би відформатувати тривалість у секундах, використовуючи такий зразок, як H: MM: SS. Поточні утиліти Java розроблені для форматування часу, але не тривалості.

Відповіді:


84

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

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

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, якщо хочете.


Ви все-таки рекомендуєте використовувати бібліотеку часу Joda під час використання Java 8?
Тім Бют

1
@ TimBüthe: Ні, я б у такому випадку почав використовувати java.time. (Хоча я не можу одразу знайти еквівалент PeriodFormatter ...)
Джон Скіт

1
PeriodFormatter не включений. Ця одна з відмінностей між API Java 8 Date Time і Joda Time.
Тім Бют

1
@RexKerr: Що ж, звичайно, все ще "використовувати Joda Time", якщо ти хочеш, звичайно. Відредагуємо ще раз. (Озираючись назад, я, як би то не було, рекомендував «Продовжити», під звуки цього звуку. Потрібно значне редагування ...)
Джон Скіт

4
@MarkJeronimus ще простіше було б використовувати Durationметоди S: duration.toHours(), duration.toMinutesPart()і duration.toSecondsPart()які доступні з Java 9. І , щоб отримати абсолютне значення можна використовувати duration.abs().
recke96

195

Якщо ви не хочете перетягувати бібліотеки, досить просто зробити себе за допомогою Форматтера або пов'язаного ярлика, наприклад. задане ціле число секунд с:

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

4
Не повинно бути цілим числом. Якщо у вас дві дати, просто вставте date1.getTime () - date2.getTime () у вищевказане рівняння, яке використовує довгий примітив і все ще працює.
Джош

Що робити, якщо різниця у часі перевищує 24 години?
Раджиш

2
@Rajish: вам доведеться запитати ОП, чого вони хочуть у такому випадку! Звичайно, ви можете розділити це на s/86400, (s%86400)/3600...дні, якщо потрібно ...
bobince

Моє рішення s > 86400 (один день): "\u221E"- нескінченність
QED

Якщо різниця в часі перевищує 24 години, вона все одно добре формує тривалість у годинах, хвилинах і секундах. Для мого випадку це як очікується.
Audrius Meskauskas

123

Я використовую таким чином DurationFormatUtils Apache common так:

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

13
Найпростіший можливий спосіб (у стилі apache commons). У вас навіть є метод formatDurationHMS, який, ймовірно, ви будете використовувати більшу частину часу.
Марко

Ідеально! Ви також можете додати інший текст у межах апострофа: DurationFormatUtils.formatDuration (durationInMillis, "m 'хвилин', s 'секунд")
mihca

29

Це простіше, оскільки Java 9. Ще 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


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
Га! Просто натисніть на стіну з нормалізацією часового поясу при використанні цього методу. Ви повинні додати, sdf.setTimeZone(TimeZone.getTimeZone("GMT+0"));перш ніж використовувати formatфункцію.
Раджиш

7

Це може бути певним хакі, але це хороше рішення, якщо хтось намагається досягти цього за допомогою 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();

}

Я просто спробував це, і я помітив , що якби я назвав формат «чч: мм: сс» , коли я воротаря є у годинах або хвилинах в форматі (наприклад Duration.ofSeconds(20)), то я б отримати UnsupportedTemporalTypeException). Я просто зняв перевірку коду, якщо різниця == 01. Я подумав, що 01це якесь маскуюче значення, яке я не розумію, ви можете це пояснити?
Groostav

Це насправді спрацювало дуже добре. У моєму випадку я навіть додав мілісекунди ... Щодо isSupportedметоду його повернення інформації, якщо є дійсне поле для відображення в людському читабельному рядку. У будь-якому випадку "маскування" насправді не є маскою. Код показує, що return value!=0lне return value!=01. Будь ласка, використовуйте копіювальну пасту наступного разу.
MrJames

1
Інтерфейс TemporalAccessorне повинен використовуватися для тривалості. TemporalAmountДля цієї мети JSR-310 розробив інтерфейс .
Meno Hochschild

1
Я рекомендую завжди використовувати великі регістри Lдля позначення longзначення. Це так просто неправильно прочитати малі літери lдля цифри 1, як щойно продемонстровано.
Оле В. В.

6

Ось ще один приклад, як форматувати тривалість. Зауважте, що цей зразок показує як позитивну, так і негативну тривалість як позитивну тривалість.

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

У цій відповіді використовуються лише 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,");
}

4

Як щодо наступної функції, яка повертає або + 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 );
    }
}

4

Існує досить простий і (IMO) елегантний підхід, принаймні, тривалістю менше 24 годин:

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

Форматорам потрібен тимчасовий об’єкт для форматування, тож ви можете створити його, додавши тривалість до LocalTime 00:00 (тобто опівночі). Це дасть вам LocalTime, який представляє тривалість з півночі до цього часу, який потім легко відформатувати в стандартній HH: mm: ss позначення. Це має перевагу в тому, що не потрібна зовнішня бібліотека, і використовує бібліотеку java.time для обчислення, а не вручну обчислювати години, хвилини та секунди.


3

Це робочий варіант.

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

2
Тривалість цього не перевищить 23: 59: 59,999.
Мішель Юнг

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

використовуючи цю функцію

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

Перейдіть на сторінку github.com/ipserc/duration, щоб отримати пакетну версію цієї функції
ipserc

Це був код, який я шукав, прийнята відповідь чудова, але у неї бракує днів, які мені знадобилися
Filnor

0

Моя бібліотека 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 форматора тривалості на основі шаблону .


0

У 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"..."Є printf/ String.formatрядком стилю Интерполятор (що дозволяє $ін'єкцію коди для роботи) З огляду на вихід 1 14:06:32.583, то fінтерпольованого рядок буде еквівалентнаString.format("1 %02d:%02d:%02d.%03d", 14, 6, 32, 583)


-1

У 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

-2

в масштабі, бібліотека не потрібна:

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
Це не чудова реклама для Scala? Бібліотека не потрібна, а стільки ж коду ...?
Адам

Я як рекурсивний підхід, але вона може бути спрощена багато: stackoverflow.com/a/52992235/3594403
dpoetzsch

-2

Я цього не бачив, тому подумав би додати:

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

Це працює лише протягом 24 годин, але я зазвичай хочу цього тривати.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.