如何处理 java.time 中的完整期间?

How to handle full period in java.time?

Period class in java.time 仅处理面向日期的部分:年、月、日。

时间部分:小时、分钟、秒?

我们如何解析和生成 ISO 8601PnYnMnDTnHnMnS 中定义的完整句点的字符串表示?例如,一天半:P1DT12H。一学年为九个月,P9M。每年我有两周零三天的假期,P17D。客户入住酒店房间2天17个半小时,P2DT17H30M.

Period class in Joda-Time处理全期。为什么不在 java.time 中?还有其他机制吗?

在 Java SE 8 中,应用程序有责任创建 class 链接 PeriodDuration(如果需要的话)。

请注意,Duration 包含秒数,而不是单独的秒数、分钟数和小时数。秒数可以超过 24 小时,因此 Duration 可以表示 "day"。但这是固定的 24 小时制。相比之下,Period 中“天”的表示是描述性的,并考虑了夏令时。Period 的状态由三个独立的字段组成 - 日、月和年。

请记住,"The customer occupied the hotel room for 2 days and seventeen and a half hours, P2DT17H30M" 有可能因 DST 切换而变得复杂。分别使用 PeriodDuration 事情很清楚 - Period 受 DST 转换影响而 Duration 不受。

在设计方面,原来的 java.time Period 确实包括小时、分钟和秒。然而,这导致需要许多方法和复杂的 Javadoc 来描述规范化和 DST 的所有可能性。通过分离概念,每个概念与时间线的交互更加清晰。请注意,这两个 class 也与 SQL 设计相关("year to month" 和 "day to second" 概念)。

目前没有计划在该区域为 Java SE 9 添加新的 class,但是不能完全排除,因为 XML/ISO-8601 允许单个组合表示。

java.time (JSR-310)相关的简答:

不,该软件包不提供解决方案。

或者,您可以使用 class Duration in the package javax.xml.datatype 来解析像 PnYnMnDTnHnMnS 这样的字符串。这也适用于自 Java-5 以来的旧 JDK 版本。示例:

// parsing
String iso = "P2Y4M30DT17H5M57.123S";
javax.xml.datatype.Duration xmlDuration =
    DatatypeFactory.newInstance().newDuration(iso);
int years = xmlDuration.getYears();
int months = xmlDuration.getMonths();
int days = xmlDuration.getDays();
int hours = xmlDuration.getHours();
int minutes = xmlDuration.getMinutes();
int fullSeconds = xmlDuration.getSeconds();
BigDecimal seconds = (BigDecimal) xmlDuration.getField(DatatypeConstants.SECONDS);

// generating ISO-string
String xml = xmlDuration.toString();
System.out.println(xml); // P2Y4M30DT17H5M57.123S

如果你要求limitations/issues,那么,这里你会得到一个列表:

  • 无法解析某些(替代)ISO 格式,如 P0001-04-20T4H

  • javax.xml.datatype.Duration 中定义的某些方法依赖于内部 Calendar 实例(已记录),因此如果 Duration 的实例成立,这些方法可能无法工作非常大的值。

  • 如果在 Calendar-实例上操作,使用小数秒可能会很尴尬,有时精度会受到限制。

  • 只有一种标准化方法使用 Calendar-实例。至少此方法以标准方式考虑了夏令时的影响。

  • 不提供格式化(更不用说本地化打印了)。

如果你想克服这些问题,那么你可以考虑一个外部库(是的,我不只是想到 Joda-Time,它的精度被限制在毫秒并且它的国际化也受到限制)。否则,包 javax.xml.datatype 的优点是省去了将外部库嵌入到 class 路径的工作。

更新:

关于评论中关于外部库的问题,我知道Joda-Time和我的库Time4J。

第一个 (Joda-Time) 提供了一个名为 ISOPeriodFormat. This class is also able to parse alternative ISO-formats (although PyyyyWwwddThhmmss is not mentioned in original ISO-8601-paper while support for PYYYY-DDD is missing). Joda-Time defines a builder-driven approach for period formatters which can also be used for printing durations (periods). Furthermore, there is a limited support for localized printing (with version 2.9.3 of Joda-Time in 13 languages). Finally the class Period offers various normalization methods (see javadoc 的特殊 class。

第二个 (Time4J) 提供 classes net.time4j.Duration 和两个格式化工具 (Duration.Formatter for pattern-based printing/parsing and net.time4j.PrettyTime for localized printing in actually 78 languages). The class Duration offers for parsing ISO-strings the static method parsePeriod(String) 以及各种规范化方法. 与 java.time (JSR-310) 的互操作性示例证明该库可以被视为和用作新 java-8-date-time-api:[= 的强大扩展29=]

// input: using java.time-package
LocalDateTime start = LocalDateTime.of(2016, 3, 7, 10, 15, 8);
LocalDateTime stop = LocalDateTime.of(2016, 6, 1, 22, 15);

// define how you measure the duration (zone correction would also be possible)
Duration<?> duration =
    TimestampInterval.between(start, stop).getDuration(
        CalendarUnit.YEARS,
        CalendarUnit.MONTHS,
        CalendarUnit.DAYS,
        ClockUnit.HOURS,
        ClockUnit.MINUTES,
        ClockUnit.SECONDS
    );

// generate standard ISO-representation
String s = duration.toStringISO();
System.out.println(s); // P2M25DT11H59M52S

// parse ISO-String and prove equality with original
System.out.println(Duration.parsePeriod(s).equals(duration)); // true

// adding duration to <start> yields <stop>
System.out.println(start.plus(duration.toTemporalAmount())); // 2016-06-01T22:15

// format in human time
System.out.println(PrettyTime.of(Locale.US).print(duration));
// output: 2 months, 25 days, 11 hours, 59 minutes, and 52 seconds

为了完整起见,我还应该提到 ocpsoft.PrettyTime,但我不确定该库是否能够处理 ISO 字符串。它是为相对时间设计的。

org.threeten.extra.PeriodDuration

ThreeTen-Extra project offers a class combining a Period and a Duration. Simply called PeriodDuration.

An amount of time in the ISO-8601 calendar system that combines a period and a duration.

This class models a quantity or amount of time in terms of a Period and Duration. A period is a date-based amount of time, consisting of years, months and days. A duration is a time-based amount of time, consisting of seconds and nanoseconds. See the Period and Duration classes for more details.

The days in a period take account of daylight saving changes (23 or 25 hour days). When performing calculations, the period is added first, then the duration.

警告:请务必阅读 以了解尝试合并 PeriodDuration 所涉及的问题。尽管这与我们的直觉相反,但在实践中这样做几乎没有意义。