如何在Java中"漂亮地打印"持续时间?

pha*_*ace 77 java time datetime date

有没有人知道一个Java库可以在几毫秒内以与C#相同的方式打印数字?

例如,123456 ms作为long将被打印为4d1h3m5s.

Rob*_*ska 86

使用PeriodFormatterBuilder,Joda Time有一个非常好的方法.

快速获胜: PeriodFormat.getDefault().print(duration.toPeriod());

例如

//import org.joda.time.format.PeriodFormatter;
//import org.joda.time.format.PeriodFormatterBuilder;
//import org.joda.time.Duration;

Duration duration = new Duration(123456); // in milliseconds
PeriodFormatter formatter = new PeriodFormatterBuilder()
     .appendDays()
     .appendSuffix("d")
     .appendHours()
     .appendSuffix("h")
     .appendMinutes()
     .appendSuffix("m")
     .appendSeconds()
     .appendSuffix("s")
     .toFormatter();
String formatted = formatter.print(duration.toPeriod());
System.out.println(formatted);
Run Code Online (Sandbox Code Playgroud)

  • 请注意这个"duration.toPeriod()"转换.如果持续时间很长,那么日期部分将保持为0.小时部分将继续增长.你将获得25h10m23s但从未获得"d".原因是在Joda的严格方法中没有完全正确的方法将小时数转换为数天.大多数情况下,如果您要比较两个时刻并想要打印它,您可以执行新的Period(t1,t2)而不是新的Duration(t1,t2).toPeriod(). (7认同)
  • @murt如果你真的想要,并且可以接受一天的标准定义,那么你可以在上面的代码中"formatter.print(duration.toPeriod().normalizedStandard())".玩得开心! (4认同)
  • 从下面的答案中可以看出,可以直接创建Period的实例,而无需先创建Duration实例,然后将其转换为Period.例如,期间=新期间(毫秒); String formatted = formatter.print(period); (2认同)

luc*_*sls 55

我已经构建了一个简单的解决方案,使用Java 8 Duration.toString()和一些正则表达式:

public static String humanReadableFormat(Duration duration) {
    return duration.toString()
            .substring(2)
            .replaceAll("(\\d[HMS])(?!$)", "$1 ")
            .toLowerCase();
}
Run Code Online (Sandbox Code Playgroud)

结果如下:

- 5h
- 7h 15m
- 6h 50m 15s
- 2h 5s
- 0.1s
Run Code Online (Sandbox Code Playgroud)

如果您不想要空格,只需删除即可replaceAll.

  • @Weltraumschaf它不太可能改变,因为它是在javadoc中指定的 (7认同)
  • @Weltraumschaf不太可能改变为[`Duration :: toString`](https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html#toString--)的输出格式根据定义明确且陈旧的[ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)标准. (7认同)
  • @Weltraumschaf你的观点通常是正确的,我自己不会依赖大多数类的 toString() 输出。但在这个非常具体的情况下,方法 **definition** 声明其输出遵循 ISO-8601 标准,正如您在该方法的 [Javadoc](https://docs.oracle.com/en/java/ javase/11/docs/api/java.base/java/time/Duration.html#toString())。所以它不像大多数类那样是一个实现细节,因此我不希望像 Java 这样成熟的语言发生这样的改变, (7认同)
  • 您永远不应该依赖于toStrong()的输出,因为这可能会在更多版本中发生变化. (5认同)
  • 如果您不需要分数,请在调用 `toLowerCase()` 之前添加 `.replaceAll("\\.\\d+", "")`。 (2认同)

Ben*_*nch 12

另一个 Java 9+ 解决方案:

private String formatDuration(Duration duration) {
    List<String> parts = new ArrayList<>();
    long days = duration.toDaysPart();
    if (days > 0) {
        parts.add(plural(days, "day"));
    }
    int hours = duration.toHoursPart();
    if (hours > 0 || !parts.isEmpty()) {
        parts.add(plural(hours, "hour"));
    }
    int minutes = duration.toMinutesPart();
    if (minutes > 0 || !parts.isEmpty()) {
        parts.add(plural(minutes, "minute"));
    }
    int seconds = duration.toSecondsPart();
    parts.add(plural(seconds, "second"));
    return String.join(", ", parts);
}

private String plural(long num, String unit) {
    return num + " " + unit + (num == 1 ? "" : "s");
}
Run Code Online (Sandbox Code Playgroud)

例如,

6 days, 21 hours, 2 minutes, 14 seconds
Run Code Online (Sandbox Code Playgroud)


nol*_*eto 11

Apache commons-lang提供了一个有用的类来完成这个,以及DurationFormatUtils

例如 DurationFormatUtils.formatDurationHMS( 15362 * 1000 ) )=> 4:16:02.000(H:m:s.millis) DurationFormatUtils.formatDurationISO( 15362 * 1000 ) )=> P0Y0M0DT4H16M2.000S,参见 ISO8601


ska*_*man 10

JodaTime具有Period可以表示这样的量的类,并且可以IsoPeriodFormatISO8601格式呈现(通过),例如PT4D1H3M5S,

Period period = new Period(millis);
String formatted = ISOPeriodFormat.standard().print(period);
Run Code Online (Sandbox Code Playgroud)

如果该格式不是您想要的格式,那么PeriodFormatterBuilder您可以组合任意布局,包括C#风格4d1h3m5s.

  • 就像一个注释,`new Period(millis).toString()`默认使用`ISOPeriodFormat.standard()`. (3认同)

Kon*_*ner 8

使用Java 8,您还可以使用其格式化toString()方法,java.time.Duration而无需使用基于ISO 8601秒表示的外部库,例如PT8H6M12.345S.

  • 不确定这种格式符合我对*pretty*print的定义.:-) (42认同)
  • 请注意,这不会打印天数 (4认同)

Adr*_*ker 8

org.threeten.extra.AmountFormats.wordBased

ThreeTen-额外的项目,该项目由Stephen Colebourne对,JSR 310,笔者维持java.time乔达时间,有一个AmountFormats与标准的Java 8日期时间类作品类。不过,它相当冗长,没有更紧凑的输出选项。

Duration d = Duration.ofMinutes(1).plusSeconds(9).plusMillis(86);
System.out.println(AmountFormats.wordBased(d, Locale.getDefault()));
Run Code Online (Sandbox Code Playgroud)

1 minute, 9 seconds and 86 milliseconds

  • @BasilBourque 牛津逗号是美国的典型代表,但有趣的是英国不是。我的库 Time4J(具有更好的国际化)在 [net.time4j.PrettyTime](http://time4j.net/javadoc-en/net/time4j/PrettyTime.html) 类中支持这种区别,并且还允许控制如果需要,紧凑的输出。 (5认同)
  • 哇,很高兴知道,我还没有看到那个功能。但有一个主要缺陷:缺少 [牛津逗号](https://en.m.wikipedia.org/wiki/Serial_comma)。 (3认同)

Men*_*ild 7

Joda-Time 的 builder-approach 的替代方案是基于模式的解决方案。这是由我的图书馆Time4J提供的。使用类Duration.Formatter 的示例(添加一些空格以提高可读性 - 删除空格将产生所需的 C# 样式):

IsoUnit unit = ClockUnit.MILLIS;
Duration<IsoUnit> dur = // normalized duration with multiple components
  Duration.of(123456, unit).with(Duration.STD_PERIOD);
Duration.Formatter<IsoUnit> f = // create formatter/parser with optional millis
  Duration.Formatter.ofPattern("D'd' h'h' m'm' s[.fff]'s'");

System.out.println(f.format(dur)); // output: 0d 0h 2m 3.456s
Run Code Online (Sandbox Code Playgroud)

此格式化程序还可以打印java.time-API 的持续时间(但是,该类型的规范化功能不那么强大):

System.out.println(f.format(java.time.Duration.ofMillis(123456))); // output: 0d 0h 2m 3.456s
Run Code Online (Sandbox Code Playgroud)

OP 的预期“123456 ms as a long will be print as 4d1h3m5s”的计算方式显然是错误的。我认为草率是原因。上面定义的相同持续时间格式化程序也可以用作解析器。下面的代码显示 "4d1h3m5s" 而对应于349385000 = 1000 * (4 * 86400 + 1 * 3600 + 3 * 60 + 5)

System.out.println(
  f.parse("4d 1h 3m 5s")
   .toClockPeriodWithDaysAs24Hours()
   .with(unit.only())
   .getPartialAmount(unit)); // 349385000
Run Code Online (Sandbox Code Playgroud)

另一种方法是使用类net.time4j.PrettyTime(这也适用于本地化输出和打印相对时间,如“昨天”、“下周日”、“4 天前”等):

String s = PrettyTime.of(Locale.ENGLISH).print(dur, TextWidth.NARROW);
System.out.println(s); // output: 2m 3s 456ms

s = PrettyTime.of(Locale.ENGLISH).print(dur, TextWidth.WIDE);
System.out.println(s); // output: 2 minutes, 3 seconds, and 456 milliseconds

s = PrettyTime.of(Locale.UK).print(dur, TextWidth.WIDE);
System.out.println(s); // output: 2 minutes, 3 seconds and 456 milliseconds
Run Code Online (Sandbox Code Playgroud)

文本宽度控制是否使用缩写。也可以通过选择适当的语言环境来控制列表格式。例如,标准英语使用 Oxform 逗号,而 UK 不使用。Time4J 的最新版本 v5.5 支持 90 多种语言,并使用基于 CLDR 存储库(行业标准)的翻译。

  • 这个 PrettyTime 比另一个答案中的要好得多!太糟糕了我没有先找到这个。 (2认同)

Tor*_*ten 7

一个Java的8版本的基础上user678573的回答

private static String humanReadableFormat(Duration duration) {
    return String.format("%s days and %sh %sm %ss", duration.toDays(),
            duration.toHours() - TimeUnit.DAYS.toHours(duration.toDays()),
            duration.toMinutes() - TimeUnit.HOURS.toMinutes(duration.toHours()),
            duration.getSeconds() - TimeUnit.MINUTES.toSeconds(duration.toMinutes()));
}
Run Code Online (Sandbox Code Playgroud)

...因为在 Java 8 中没有 PeriodFormatter 并且没有像 getHours、getMinutes、...

我很高兴看到 Java 8 的更好版本。


小智 6

以下是使用纯JDK代码的方法:

import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;

long diffTime = 215081000L;
Duration duration = DatatypeFactory.newInstance().newDuration(diffTime);

System.out.printf("%02d:%02d:%02d", duration.getDays() * 24 + duration.getHours(), duration.getMinutes(), duration.getSeconds()); 
Run Code Online (Sandbox Code Playgroud)


eri*_*eal 6

Java 9+

Duration d1 = Duration.ofDays(0);
        d1 = d1.plusHours(47);
        d1 = d1.plusMinutes(124);
        d1 = d1.plusSeconds(124);
System.out.println(String.format("%s d %sh %sm %ss", 
                d1.toDaysPart(), 
                d1.toHoursPart(), 
                d1.toMinutesPart(), 
                d1.toSecondsPart()));
Run Code Online (Sandbox Code Playgroud)

2 d 1h 6m 4s