在Java 8 / jsr310中格式化持续时间

44

我正在将一个项目从Joda-Time转换为Java8的本机时间库,并且遇到了一个问题。

我找不到Duration的格式化程序。我想自定义字符串格式,例如HHH+MM,其中75小时15分钟的持续时间将格式化为"75+15"

使用Joda-Time很容易实现,只需将其转换为Period并使用PeriodFormatter即可,但是我无法在Java8中找到这种类型的类。我有什么遗漏吗?


@MartinSchröder 那个问题是关于格式化持续时间(以任何形式),并且考虑到它早于Java 8,它并不特别涉及格式化java.time.Duration。在这个问题中,OP正在询问他是否错过了java.time中的格式化程序,用于java.time.Duration。因此,这些问题是相关的,但不是重复的。 - Mark Rotteveel
对我来说,duration.get().toMillis() 起作用了。我之前尝试的是 duration.toMillis - FBI Surveillance Van
5个回答

44

Java 9及以上版本:Duration::to…Part方法

在Java 9中,Duration类新增了to…Part方法,用于返回持续时间的天数、小时数、分钟数、秒数、毫秒数/纳秒数的各个部分。您可以在这个预发布的OpenJDK源代码中查看。

例如,对于一个持续时间为49小时30分钟20.123秒的时间段...

  • toNanosPart() = 123000000
  • toMillisPart() = 123
  • toSecondsPart() = 20
  • toMinutesPart() = 30
  • toHoursPart() = 1
  • toDaysPart() = 2

请注意,这里的“天”是指24小时的块,忽略日历上的日期。如果你关心日期,应该使用Period类。

我不知道是否增加了任何其他格式化程序功能。但至少您将能够更方便地从通过这些新的getter方法获得的数字中生成自己的字符串。

Java 8

奇怪的是,在Java 8中的java.time框架的第一版发布中,并没有包含这些值的方便的getter方法,这是该框架设计中非常少见的疏忽之一。

请参见相关问题: 为什么我无法在java.time中获得分钟或小时的持续时间?.


25

Jsr-310没有持续时间格式化程序,与JodaTime不同。并非JodaTime的每个功能都被移植到JSR-310中(例如PeriodType也不是)。相反,JSR-310具有一些在JodaTime中不可用的特性(例如本地化的工作日数字或使用调整器的策略模式方法)。

可能会发生Java 9将引入某种内置期间格式的情况(从S. Colebourne读了一些关于此事的内容)。

结论:JSR-310和JodaTime彼此不完全兼容,因此可能需要大量工作。我不会急于尽快迁移。您是否需要JSR-310提供的特殊功能,这些功能JodaTime没有提供?

附加说明:您还应该知道,joda period(包括从年到秒的所有单位)与jsr310-period(仅限年、月、日)或jsr310-duration(仅限小时、分钟、秒和分数秒)不完全兼容。


谢谢,我想我只能自己动手了。这对我来说是一个业余项目,所以旅程比目的地更重要。:) 即使它缺少一些功能,我认为 JSR-310 最终将在其他库(如 jaxb)中拥有更好的开箱即用支持。 - Chad Lowe
就个人而言,我认为将jsr 310与其他库集成的方式并不是很好。例如,在Java 8中不会发生JAXB集成。但是有可用的解决方法,就像对于JodaTime一样,因此JAXB并不是一个难以克服的障碍。 - Meno Hochschild
@MenoHochschild 您还可以获取由持续时间表示的天数(除了小时、分钟、秒和纳秒)。 - assylias
@assylias 没错。我忽略了这个小细节。但事实是,JSR-310中的Duration不接受所有单位。更糟糕的是,由于内部转换为仅秒和纳秒,许多允许在持续时间构造中使用的单位在查询中并不真正得到支持(或者只能以混乱的方式 - 请参见您的示例)。基本问题是您放入Duration中的内容并不是您获得的内容。 - Meno Hochschild

20

没有内置的方法,但您可以访问小时/分钟数,而无需手动计算。 您的特定格式可能如下所示:

Duration d = Duration.of(75, HOURS).plusMinutes(15);
long hours = d.toHours(); //75
long minutes = d.minusHours(hours).toMinutes(); //15
String HH_PLUS_MM = hours + "+" + minutes; //75+15
System.out.println(HH_PLUS_MM);

如果保证时长小于24小时,您也可以使用这个技巧:

String hhPlusMm = LocalTime.MIDNIGHT.plus(d).format(DateTimeFormatter.ofPattern("HH+mm"));

查询时间间隔的混乱程度很高,但这不是你的错。在构建时间间隔时可以使用小时和分钟,但无法直接在标准方法get(TemporalUnit)中使用这些单位 :-( 我知道为什么要开发自己的时间库。 - Meno Hochschild
@MenoHochschild 这个API非常适合将Duration用作DateTime的偏移量,但作为独立对象则不太适用。如果您有想法,可以为310做出贡献(尽管现在对于Java 8来说已经太晚了)! - assylias
@assylias,你是如何导入HOURS时间单位的? - IgorGanapolsky
4
import static java.time.temporal.ChronoUnit.HOURS;import static java.time.temporal.ChronoUnit.HOURS; - assylias

13
您可以使用commons-lang3-time中的DurationFormatUtils(对于分钟,您必须使用“mm”,因为格式与SimpleDateFormat相同): DurationFormatUtils.formatDuration(interval.toMillis(), "HHH+mm") 不幸的是,我没有找到排除空部分的方法,就像在我的情况下,天数或小时可能为0一样,因此我仍然必须自己编写。
更新:我已经在Apache commons.lang.time.DurationFormatUtils JIRA上开了一个问题

6
使用DurationFormatUtils.formatDurationWords(interval.toMillis(), true, true) 可以排除空字段。 - wittyameta
最好的解决方案是在Java 8上使用,非常感谢! - Brendon Iwata

1

我知道这是一个老问题,但最近我也遇到了同样的情况。实际上应该有比这更好的解决方案,但这个对我有效:

public static String millisToElapsedTime(long millis){
    DateFormat fmt = new SimpleDateFormat(":mm:ss.SSS");
    fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
    return (millis/3600000/*hours*/)+fmt.format(new Date(millis));
}

然后,你可以添加这个:

然后,你可以添加这个:

public static String durationToElapsedTime(Duration d){
    return millisToElapsedTime(d.toMillis());
}

在这里,它记录了12:00:00.022的时间,持续时间为22毫秒。 - flup
抱歉 - SDF字符串中有一个漏洞 - 请尝试使用大写字母“HH”。 - CodeBlind
经过进一步的思考,我意识到这仅适用于不超过24小时的时间。我已经修复了它,可以允许超过24小时的时间。 - CodeBlind

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接