如何在Java中格式化持续时间?(例如,格式H:MM:SS)


164

我想使用H:MM:SS之类的格式以秒为单位格式化持续时间。Java中的当前实用程序旨在格式化时间而不是持续时间。

Answers:


84

如果您使用的Java版本早于8 ...,则可以使用Joda TimePeriodFormatter。如果您确实有一个持续时间(即经过的时间,没有参考日历系统),那么您可能应该Duration大部分时间都在使用-然后可以致电toPeriod(指定PeriodType要反映25小时是否变为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;
}

如果烦人的话,格式化这种方式相当简单。一般而言,要进行解析将变得更加困难……当然,即使您愿意,即使Java 8也可以使用Joda Time。


在使用Java 8时,您仍然建议使用Joda Time库吗?
TimBüthe2014年

1
@TimBüthe:不,在这种情况下,我将开始使用java.time。(尽管我无法立即找到相当于PeriodFormatter的...)
Jon Skeet

1
不包括PeriodFormatter。Java 8 Date Time API和Joda Time之间的差异之一。
TimBüthe2014年

1
@RexKerr:好吧,当然,如果您愿意,仍然可以使用“ Joda Time”。将再次编辑。(回想一下,无论如何,我本来应该向您推荐“持续时间”。需要进行大量编辑...)
乔恩·斯凯特

4
@MarkJeronimus甚至更容易将使用Duration小号方法:duration.toHours()duration.toMinutesPart()duration.toSecondsPart()这是因为Java的9可用并且得到绝对值可以使用duration.abs()
recke96 '18

195

如果您不想拖入库,那么使用格式化程序或相关的快捷方式(例如)就足够简单了。给定的整数秒数s:

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

4
不必是整数。如果您有两个日期,只需将date1.getTime()-date2.getTime()插入上述使用长原语的方程式中即可使用。
乔什

如果时差超过24小时怎么办?
拉吉什

2
@Rajish:在这种情况下,您必须问问OP他们想要什么!当然s/86400, (s%86400)/3600...,如果需要的话,您可以将其分成几天……
bobince 2011年

我对s > 86400(一天)的解决方案:"\u221E"-无穷大
2012年

如果时间差超过24小时,则仍然可以很好地格式化持续时间,以小时,分钟和秒为单位。对于我的用例,这是预期的。
Audrius Meskauskas 2015年

122

我像这样使用Apache common的DurationFormatUtils

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

13
最简单的方法(以Apache Commons样式)。您甚至拥有formatDurationHMS方法,您可能会在大多数时间使用它。
Marko

完善!您还可以在撇号内添加其他文本:DurationFormatUtils.formatDuration(durationInMillis,“
m'minutes

29

从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


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

}

我只是尝试了一下,然后发现如果我没有任何时间或格式来格式化“ hh:mm:ss” (例如Duration.ofSeconds(20)),那么我会得到UnsupportedTemporalTypeException)。我只是删除了代码检查是否存在差异== 01。我认为这01是我不完全理解的某种掩盖价值,您能解释一下吗?
Groostav 2015年

这实际上效果很好。就我而言,我什至增加了毫秒...关于该isSupported方法,如果在人类可读的字符串上显示有效字段,则返回信息。无论如何,“遮罩”实际上不是遮罩。代码return value!=0l未显示return value!=01。请下次使用复制粘贴。
MrJames '16

1
TemporalAccessor不应在长时间内滥用该接口。JSR-310 TemporalAmount为此目的设计了接口。
Meno Hochschild

1
我建议您始终使用大写字母L表示long值。l正如刚才所展示的,误读数字1的小写字母非常容易。
Ole VV

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

格式化程序需要一个时间对象进行格式化,因此您可以通过将持续时间添加到本地时间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,此操作将失败。
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();
}

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

该代码演示了处理小时溢出和标志处理的功能,另请参见基于pattern的duration-formatter的API 。


0

在斯卡拉(我看到了其他尝试,但没有留下深刻的印象):

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)


0

使用这个功能

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

-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

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

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小时,但这就是我通常想要的持续时间。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.