解析AMAZON.DURATION插槽类型的ISO-8601持续时间值

Upi*_*pio 6 java duration period java-time alexa-skills-kit

java.time库是否提供了解析整个ISO-8601持续时间规范的综合方法?

持续时间Alexa Slot Type引用列出了使用AMAZON.DURATION槽类型时要预期的一些示例字符串.所有字符串都在ISO-8601持续时间内,但P2YT3H10无法通过java.time.Period或解析java.time.Duration.

Seq(
 "PT10M",
 "PT5H",
 "P3D",
 "PT45S",
 "P8W",
 "P7Y",
 "PT5H10M",
 "P2YT3H10"
).map { s =>
 s -> Try {
   try {
     Period.parse(s)
   } catch {
     case ex: Throwable => Duration.parse(s)
   }
 }.map(x => x.toString -> x.getClass.getSimpleName)
}
.foreach(println)
Run Code Online (Sandbox Code Playgroud)

结果:

(PT10M,Success((PT10M,Duration)))
(PT5H,Success((PT5H,Duration)))
(P3D,Success((P3D,Period)))
(PT45S,Success((PT45S,Duration)))
(P8W,Success((P56D,Period)))
(P7Y,Success((P7Y,Period)))
(PT5H10M,Success((PT5H10M,Duration)))
(P2YT3H10,Failure(java.time.format.DateTimeParseException: Text cannot be parsed to a Duration))
Run Code Online (Sandbox Code Playgroud)

小智 6

PS:您的输入缺少最后一个字段的指示符(P2YT3H10- 2 年 3 小时和 10 什么?)。所以,在下面的代码中,我只是假设它是M(分钟) - 在10.


java.timeAPI已分离的ISO8601的持续时间在2类

  • java.time.Period,可以处理基于日期的字段(年、月和日)
  • java.time.Duration,可以处理基于时间的字段*(实际上,根据 javadoc:“此类以秒和纳秒为单位对数量或时间进行建模。它可以使用其他基于持续时间的单位进行访问,例如分钟和小时” )

不幸的是,这些类无法处理具有基于日期和时间的字段的 ISO8601 持续时间。

一种替代方法是使用ThreeTen Extra项目,其中包含一些java.timeAPI扩展。有了这个库,你可以使用org.threeten.extra.PeriodDurationclass,它可以解析完整的 ISO8601 持续时间:

PeriodDuration pd = PeriodDuration.parse("P2YT3H10M");
Run Code Online (Sandbox Code Playgroud)

然后你可以得到各自的PeriodDuration从它:

System.out.println(pd.getPeriod()); // P2Y
System.out.println(pd.getDuration()); // PT3H10M
Run Code Online (Sandbox Code Playgroud)

另一种选择是分裂String,并解析都PeriodDuration单独:

// split in 2 parts
String input = "P2YT3H10M";
String[] v = input.split("T");
Period p;
Duration d;
if (v.length == 1) { // has only date-based fields
    p = Period.parse(input);
    d = Duration.ZERO;
} else {
    if ("P".equals(v[0])) { // has only time-based fields
        p = Period.ZERO;
    } else {
        p = Period.parse(v[0]);
    }
    d = Duration.parse("PT" + v[1]);
}
Run Code Online (Sandbox Code Playgroud)

*ADuration也接受days,并且这些总是被认为有 24 小时(在 a 中Period,由于夏令时效应,一天不一定有 24 小时)。


Men*_*ild 5

Hugos答案仅与ISO-8601标准的一个细节方面有关,即日期和时间部分与"T"分离器的组合.这个细节确实不受java.time外部库Threeten-Extra(使用PeriodDuration版本v1.2中的类)的支持.然而:

ISO-8601标准描述了更多变化.

一种变化是以"P8W"形式使用周.两者java.time和Threeten-Extra会自动将其更改为"P56D",但在解析和格式化时不要将其保留为与周相关的形式.查看时间组件时可以获得类似的视图.java.time.Duration无法直接存储小时或分钟,但会自动将其转换为秒.当执行格式化自动规范化时也是如此.例:

System.out.println(Duration.parse("PT4200S")); // PT1H10M
Run Code Online (Sandbox Code Playgroud)

所以在这两种情况下:在构造过程中放入的内容并不总是在格式化时得到的内容.

其他要点:

  • 在ISO-8601持续时间表示(如第4.4.4.2规定的)往往被称之为"持续时间"(即使日期相关的),同时java.time使用两个不同的方面,即PeriodDuration(或日期相关或与时间相关).
  • 与周相关的持续时间是分开处理的,因为描述了ISO-8601符号中的两种标准方式:"PnnYnnMnnDTnnHnnMnnS"或"PnnW".
  • ISO-8601规范中不存在符号(参见第3.4.2节:符号"n"定义为正整数或零),同时java.time允许在表示内部使用符号并将符号解释为与组件相关的符号.请注意,XML-Schema仅处理整个持续时间表达式之前的符号(在"P"之前),这不是与组件相关的,而是与持续时间相关的.
  • 也可以以基本格式或扩展格式指定持续时间的替代表示:"PYYYYMMDDThhmmss"."PYYYY-MM-DDTHH:MM:SS".
  • 小数秒的处理:ISO-8601说明逗号或点的使用(甚至明确优先使用逗号),而java.time.Duration只支持点.

最后我们可以说:

ISO-8601标准仅部分支持java.time(和Threeten-Extra,它仅支持类的组合Periodjava.time.Duration附加细节).

真正的ISO-8601支持的替代解决方案:

如果您想要甚至需要克服当时的限制,java.time您可以使用我的库Time4J,它支持ISO-8601的所有其他细节.请参阅net.time4j.Duration类的API .以下示例还显示了兼容性java.time:

Duration<CalendarUnit> d1 = Duration.parseCalendarPeriod("P8W");
System.out.println(d1); // P8W
System.out.println(d1.getPartialAmount(CalendarUnit.WEEKS)); // 8
System.out.println(Duration.Formatter.ofPattern(CalendarUnit.class, "W' weeks'").format(d1)); // 8 weeks
System.out.println(PrettyTime.of(Locale.GERMAN).print(d1)); // 8 Wochen
LocalDate ld = LocalDate.of(2017, 9, 17);
System.out.println(PlainDate.from(ld).plus(d1)); // 2017-11-12
System.out.println(PlainDate.of(2017, 9, 17).plus(d1)); // 2017-11-12

Duration<IsoUnit> d2 = Duration.parsePeriod("P2DT5H10M");
LocalDateTime ldt = LocalDateTime.of(2017, 9, 17, 19, 15);
System.out.println(PlainTimestamp.from(ldt).plus(d2)); // 2017-09-20T00:25
System.out.println(PlainTimestamp.of(2017, 9, 17, 19, 15).plus(d2)); // 2017-09-20T00:25
System.out.println(PrettyTime.of(Locale.GERMAN).print(d2)); // 2 Tage, 5 Stunden und 10 Minuten

Duration<IsoUnit> d3 = Duration.parsePeriod("P0001-01-02T05:10:04");
System.out.println(d3); // P1Y1M2DT5H10M4S
LocalDateTime ldt = LocalDateTime.of(2017, 9, 17, 19, 15);
System.out.println(PlainTimestamp.from(ldt).plus(d3)); // 2018-10-20T00:25:04
Run Code Online (Sandbox Code Playgroud)

附注:实际上有86种语言可以格式化持续时间.