Java 中的 ISO 8601 时间间隔解析

问题描述 投票:0回答:5

ISO 8601 定义了表示时间间隔的语法。

时间间隔有四种表达方式:

  • 开始和结束,例如“2007-03-01T13:00:00Z/2008-05-11T15:30:00Z”
  • 开始时间和持续时间,例如“2007-03-01T13:00:00Z/P1Y2M10DT2H30M”
  • 持续时间和结束时间,如“P1Y2M10DT2H30M/2008-05-11T15:30:00Z”
  • 仅持续时间,例如“P1Y2M10DT2H30M”,带有附加上下文信息

如果最终值缺少任何元素,则假定它们与起始值相同(包括时区)。该标准的这一功能允许简洁地表示时间间隔。例如,两小时会议的日期(包括开始和结束时间)可以简单地显示为“2007-12-14T13:30/15:30”,其中“/15:30”意味着“/2007-12- 14T15:30”(与开始日期相同),或每月计费周期的开始和结束日期为“2008-02-15/03-14”,其中“/03-14”意味着“/2008-03-14”(与开始的年份相同)。

此外,重复间隔是通过在间隔表达式的开头添加“R[n]/”形成的,其中R用作字母本身,[n]替换为重复次数。省略 [n] 的值意味着无限次的重复。因此,要从“2008-03-01T13:00:00Z”开始重复“P1Y2M10DT2H30M”间隔五次,请使用“R5/2008-03-01T13:00:00Z/P1Y2M10DT2H30M”。

我正在寻找一个好的Java解析器(如果可能与Joda-Time库兼容)来解析这个语法。有什么好的图书馆推荐吗?

java parsing time jodatime intervals
5个回答
35
投票

java.time

Java 8 及更高版本中内置的 java.time 框架有一个

Duration.parse
方法,用于解析 ISO 8601 格式的持续时间

java.time.Duration d = java.time.Duration.parse("PT1H2M34S");
System.out.println("Duration in seconds: " + d.get(java.time.temporal.ChronoUnit.SECONDS));

打印

Duration in seconds: 3754


18
投票

对于可能被限制使用第 3 方库(许可原因或其他原因)的项目中的任何人,Java 本身至少提供了部分此功能,从 Java 1.6(或更早版本?)开始,使用 javax.xml。 datatype.DatatypeFactory.newDuration(String) 方法和 Duration 类。 DatatypeFactory.newDuration(String) 方法将解析“PnYnMnDTnHnMnS”格式的字符串。 这些类旨在用于 XML 操作,但由于 XML 使用 ISO 8601 时间表示法,因此它们也可用作方便的持续时间解析实用程序。

示例:

import javax.xml.datatype.*;

Duration dur = DatatypeFactory.newInstance().newDuration("PT5H12M36S");
int hours = dur.getHours(); // Should return 5

除了您列出的第四种格式之外,我个人没有使用过任何持续时间格式,因此我无法保证它是否成功解析它们。


10
投票

我想您已经尝试过 Joda-Time 了吗?通过

Interval.parse(Object)
输入问题中的示例字符串表明它可以处理“开始和结束”、“开始和持续时间”以及“持续时间和结束”,但不能处理隐含字段或重复。

2007-03-01T13:00:00Z/2008-05-11T15:30:00Z => from 2007-03-01T13:00:00.000Z to 2008-05-11T15:30:00.000Z
2007-03-01T13:00:00Z/P1Y2M10DT2H30M       => from 2007-03-01T13:00:00.000Z to 2008-05-11T15:30:00.000Z
P1Y2M10DT2H30M/2008-05-11T15:30:00Z       => from 2007-03-01T13:00:00.000Z to 2008-05-11T15:30:00.000Z
2007-12-14T13:30/15:30                    => java.lang.IllegalArgumentException: Invalid format: "15:30" is malformed at ":30"
R5/2008-03-01T13:00:00Z/P1Y2M10DT2H30M    => java.lang.IllegalArgumentException: Invalid format: "R5"

据我所知,唯一的其他综合日期/时间库是JSR-310,它似乎无法处理这样的间隔。

此时,在 Joda-Time 之上构建自己的改进可能是您最好的选择,抱歉。除了 Joda-Time 已支持的格式之外,您还需要处理任何特定的 ISO 间隔格式吗?


10
投票

唯一能够对您想要的区间解析的所有功能进行建模的库实际上是我的库Time4J(范围模块)。例子:

// case 1 (start/end) System.out.println(MomentInterval.parseISO("2012-01-01T14:15Z/2014-06-20T16:00Z")); // output: [2012-01-01T14:15:00Z/2014-06-20T16:00:00Z) // case 1 (with some elements missing at end component and different offset) System.out.println(MomentInterval.parseISO("2012-01-01T14:15Z/08-11T16:00+00:01")); // output: [2012-01-01T14:15:00Z/2012-08-11T15:59:00Z) // case 1 (with missing date and offset at end component) System.out.println(MomentInterval.parseISO("2012-01-01T14:15Z/16:00")); // output: [2012-01-01T14:15:00Z/2012-01-01T16:00:00Z) // case 2 (start/duration) System.out.println(MomentInterval.parseISO("2012-01-01T14:15Z/P2DT1H45M")); // output: [2012-01-01T14:15:00Z/2012-01-03T16:00:00Z) // case 3 (duration/end) System.out.println(MomentInterval.parseISO("P2DT1H45M/2012-01-01T14:15Z")); // output: [2011-12-30T12:30:00Z/2012-01-01T14:15:00Z) // case 4 (duration only, in standard ISO-format) Duration<IsoUnit> isoDuration = Duration.parsePeriod("P2DT1H45M"); // case 4 (duration only, in alternative representation) Duration<IsoUnit> isoDuration = Duration.parsePeriod("P0000-01-01T15:00"); System.out.println(isoDuration); // output: P1M1DT15H

一些备注:

  • 其他间隔类也具有类似的解析功能,例如包中的

    DateInterval

    TimestampInterval
     
    net.time4j.range.

  • 仅处理持续时间(也可以跨越日历和时钟单位),另请参阅

    javadoc。还有格式化功能,请参阅嵌套类Duration.Formatter

    或本地化版本
    net.time4j.PrettyTime
    (实际上有86种语言)。

  • Java-8(

    java.time

    -package)提供了互操作性,但 Joda-Time 不提供互操作性。例如:可以通过 
    MomentInterval
    getStartAsInstant()
     轻松查询 
    getEndAsInstant()
     的开始或结束部分。

重复间隔由类

IsoRecurrence支持。示例:

IsoRecurrence<MomentInterval> ir = IsoRecurrence.parseMomentIntervals("R5/2008-03-01T13:00:00Z/P1Y2M10DT2H30M"); ir.intervalStream().forEach(System.out::println);

输出:

[2008-03-01T13:00:00Z/2009-05-11T15:30:00Z) [2009-05-11T15:30:00Z/2010-07-21T18:00:00Z) [2010-07-21T18:00:00Z/2011-10-01T20:30:00Z) [2011-10-01T20:30:00Z/2012-12-11T23:00:00Z) [2012-12-11T23:00:00Z/2014-02-22T01:30:00Z)
    

0
投票
Spring 有一个 ISO 6801 持续时间的解析器(原始问题中的第四种情况)。不过我还没有测试过其他三种情况

import org.springframework.format.annotation.DurationFormat; import org.springframework.format.datetime.standard.DurationFormatterUtils; Duration duration = DurationFormatterUtils.parse("P1DT1H", DurationFormat.Style.ISO8601);
    
© www.soinside.com 2019 - 2024. All rights reserved.