解析 AMAZON.DURATION 插槽类型的 ISO-8601 持续时间值
Parsing the ISO-8601 duration values of the AMAZON.DURATION slot type
java.time
库是否提供了一种统一的方式来解析整个 ISO-8601 Duration Specification?
Alexa Slot Type reference for duration 列出了使用 AMAZON.DURATION 插槽类型时预期的一些示例字符串。所有字符串都在 ISO-8601 Duration 中,但是 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)
结果:
(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))
PS:您输入的最后一个字段缺少指示符(P2YT3H10
- 2 年 3 小时 10什么?)。因此,在下面的代码中,我只是假设它是 M
(分钟)——在 10
.
之后如果没有指示符就无法工作
java.time
API 隔了ISO8601时长 in 2 classes:
java.time.Period
,可以处理date-based fields (years, months and days)
java.time.Duration
,可以处理 time-based fields*(实际上,根据 javadoc:"This class models a quantity or amount of time in terms of seconds and nanoseconds. It can be accessed using other duration-based units, such as minutes and hours")
不幸的是,那些 类 无法处理基于日期和时间的字段的 ISO8601 持续时间。
一种替代方法是使用 ThreeTen Extra project, that contains some extensions for java.time
API. With this lib, you can use the org.threeten.extra.PeriodDuration
class,它可以解析完整的 ISO8601 持续时间:
PeriodDuration pd = PeriodDuration.parse("P2YT3H10M");
然后就可以从中得到各自的Period
和Duration
:
System.out.println(pd.getPeriod()); // P2Y
System.out.println(pd.getDuration()); // PT3H10M
另一种方法是拆分 String
,并分别解析 Period
和 Duration
:
// 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]);
}
*A Duration
也接受 天 ,那些总是被认为有 24 小时(在 Period
中,一天 由于夏令时的影响)。
Hugos 的回答只涉及 ISO-8601 标准的一个细节方面,即日期和时间部分与“T”分隔符的组合。 java.time
确实不支持此细节,但外部库 Threeten-Extra 支持(使用版本 v1.2 中的 class PeriodDuration
)。然而:
ISO-8601 标准描述了更多变化。
一种变体是以“P8W”形式使用周。 java.time
和 Threeten-Extra 都会自动将其更改为“P56D”,但在解析和格式化时不要将其保留为与星期相关的形式。查看时间分量时可以获得类似的视图。 java.time.Duration
无法直接存储小时或分钟,但会自动将其转换为秒。格式化时也会执行自动规范化。示例:
System.out.println(Duration.parse("PT4200S")); // PT1H10M
所以在这两种情况下:你在构建过程中放入的内容并不总是你在格式化时得到的内容。
其他要点:
- ISO-8601 中的持续时间表示(如第 4.4.4.2 节中所述) 始终称为“持续时间”(即使与日期相关),而
java.time
使用两个不同的术语,即 Period
和 Duration
(与日期相关或与时间相关)。
- 周相关持续时间 单独处理,因为描述了 ISO-8601 表示法中的两种标准方式:“PnnYnnMnnDTnnHnnMnnS”或“PnnW”。
- 符号在 ISO-8601 规范中不存在(参见第 3.4.2 节:符号“n”定义为正整数或零)而
java.time
甚至在表示内部也允许符号并将符号解释为与组件相关。请注意,XML-Schema 仅处理整个持续时间表达式之前的符号(“P”之前),这与组件无关,但与持续时间相关。
- 持续时间的替代表示法 也以基本格式或扩展格式指定:“PYYYYMMDDThhmmss” resp。 “PYYYY-MM-DDThh:mm:ss”。
- 小数秒的处理:ISO-8601 谈到逗号或点的使用(甚至明确更喜欢逗号),而
java.time.Duration
只支持点。
最后我们可以声明:
java.time
仅部分支持 ISO-8601 标准(而 Threeten-Extra 仅支持 classes Period
和 java.time.Duration
的组合作为额外的细节)。
真正支持 ISO-8601 的替代解决方案:
如果您想甚至需要克服 java.time
中的限制,那么您可以使用我的库 Time4J which supports all the additional details of ISO-8601. See the API of class net.time4j.Duration。以下示例还显示了与 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
旁注:实际上 86 种语言都可以设置持续时间的格式。
Java 8 Duration只允许天、时、分、秒:
接受的格式基于 ISO-8601 持续时间格式PnDTnHnMn.nS
https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html#parse-java.lang.CharSequence-
java.time
库是否提供了一种统一的方式来解析整个 ISO-8601 Duration Specification?
Alexa Slot Type reference for duration 列出了使用 AMAZON.DURATION 插槽类型时预期的一些示例字符串。所有字符串都在 ISO-8601 Duration 中,但是 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)
结果:
(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))
PS:您输入的最后一个字段缺少指示符(P2YT3H10
- 2 年 3 小时 10什么?)。因此,在下面的代码中,我只是假设它是 M
(分钟)——在 10
.
java.time
API 隔了ISO8601时长 in 2 classes:
java.time.Period
,可以处理date-based fields (years, months and days)java.time.Duration
,可以处理 time-based fields*(实际上,根据 javadoc:"This class models a quantity or amount of time in terms of seconds and nanoseconds. It can be accessed using other duration-based units, such as minutes and hours")
不幸的是,那些 类 无法处理基于日期和时间的字段的 ISO8601 持续时间。
一种替代方法是使用 ThreeTen Extra project, that contains some extensions for java.time
API. With this lib, you can use the org.threeten.extra.PeriodDuration
class,它可以解析完整的 ISO8601 持续时间:
PeriodDuration pd = PeriodDuration.parse("P2YT3H10M");
然后就可以从中得到各自的Period
和Duration
:
System.out.println(pd.getPeriod()); // P2Y
System.out.println(pd.getDuration()); // PT3H10M
另一种方法是拆分 String
,并分别解析 Period
和 Duration
:
// 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]);
}
*A Duration
也接受 天 ,那些总是被认为有 24 小时(在 Period
中,一天
Hugos 的回答只涉及 ISO-8601 标准的一个细节方面,即日期和时间部分与“T”分隔符的组合。 java.time
确实不支持此细节,但外部库 Threeten-Extra 支持(使用版本 v1.2 中的 class PeriodDuration
)。然而:
ISO-8601 标准描述了更多变化。
一种变体是以“P8W”形式使用周。 java.time
和 Threeten-Extra 都会自动将其更改为“P56D”,但在解析和格式化时不要将其保留为与星期相关的形式。查看时间分量时可以获得类似的视图。 java.time.Duration
无法直接存储小时或分钟,但会自动将其转换为秒。格式化时也会执行自动规范化。示例:
System.out.println(Duration.parse("PT4200S")); // PT1H10M
所以在这两种情况下:你在构建过程中放入的内容并不总是你在格式化时得到的内容。
其他要点:
- ISO-8601 中的持续时间表示(如第 4.4.4.2 节中所述) 始终称为“持续时间”(即使与日期相关),而
java.time
使用两个不同的术语,即Period
和Duration
(与日期相关或与时间相关)。 - 周相关持续时间 单独处理,因为描述了 ISO-8601 表示法中的两种标准方式:“PnnYnnMnnDTnnHnnMnnS”或“PnnW”。
- 符号在 ISO-8601 规范中不存在(参见第 3.4.2 节:符号“n”定义为正整数或零)而
java.time
甚至在表示内部也允许符号并将符号解释为与组件相关。请注意,XML-Schema 仅处理整个持续时间表达式之前的符号(“P”之前),这与组件无关,但与持续时间相关。 - 持续时间的替代表示法 也以基本格式或扩展格式指定:“PYYYYMMDDThhmmss” resp。 “PYYYY-MM-DDThh:mm:ss”。
- 小数秒的处理:ISO-8601 谈到逗号或点的使用(甚至明确更喜欢逗号),而
java.time.Duration
只支持点。
最后我们可以声明:
java.time
仅部分支持 ISO-8601 标准(而 Threeten-Extra 仅支持 classes Period
和 java.time.Duration
的组合作为额外的细节)。
真正支持 ISO-8601 的替代解决方案:
如果您想甚至需要克服 java.time
中的限制,那么您可以使用我的库 Time4J which supports all the additional details of ISO-8601. See the API of class net.time4j.Duration。以下示例还显示了与 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
旁注:实际上 86 种语言都可以设置持续时间的格式。
Java 8 Duration只允许天、时、分、秒:
接受的格式基于 ISO-8601 持续时间格式PnDTnHnMn.nS
https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html#parse-java.lang.CharSequence-