在Duration步骤中迭代ZonedDateTime间隔

时间:2018-04-20 06:34:23

标签: java datetime java-8 java-time

问题

给定开始和结束时间戳以及持续时间我希望以持续时间的步长迭代该时间间隔。持续时间应以ISO 8601表示法指定。应根据时区考虑夏令时。

示例代码:

// start/end at switch from summer to winter time
ZonedDateTime startTimestamp = ZonedDateTime.of( LocalDateTime.of(2018, 10, 28, 0, 0), ZoneId.of("CET"));
ZonedDateTime endTimestamp = startTimestamp.plusHours(5);

Duration duration = Duration.parse( "PT1H");
while( startTimestamp.isBefore(endTimestamp)) {
    System.out.println( startTimestamp);
    startTimestamp = startTimestamp.plus( duration);
}

结果是:

2018-10-28T00:00+02:00[CET]
2018-10-28T01:00+02:00[CET]
2018-10-28T02:00+02:00[CET]
2018-10-28T02:00+01:00[CET]
2018-10-28T03:00+01:00[CET]

问题是,只要持续时间是最多天数,这就有效。从持续时间解析器文档:

  

然后有四个部分,每个部分由数字和后缀组成。这些部分的ASCII为“D”,“H”,“M”和“S”的后缀为天,小时,分钟和秒,以大写或小写形式接受。

ISO 8601标准规定持续时间也可能是数月和数年。

  

持续时间定义时间间隔内的干预时间量   用格式P [n] Y [n] M [n] DT [n] H [n] M [n] S或P [n] W

表示

问题

考虑到周,月,年的日历元素,您如何在ISO 8601持续时间内通过ZonedDateTime间隔进行正确迭代?

月份示例:

Start: 01.01.2018
End: 01.01.2019

我希望每个月都能获得每一个月1日。将P1M指定为持续时间当然会抛出此异常:

Exception in thread "main" java.time.format.DateTimeParseException: 
Text cannot be parsed to a Duration

2 个答案:

答案 0 :(得分:3)

To work with date related fields (years, months and days), you must use a java.time.Period. Example:

ZonedDateTime startTimestamp = ZonedDateTime.of(LocalDateTime.of(2018, 1, 1, 0, 0), ZoneId.of("CET"));
ZonedDateTime endTimestamp = startTimestamp.plusMonths(5);

Period period = Period.parse("P1M");
while (startTimestamp.isBefore(endTimestamp)) {
    System.out.println(startTimestamp);
    startTimestamp = startTimestamp.plus(period);
}

This prints:

2018-01-01T00:00+01:00[CET]
2018-02-01T00:00+01:00[CET]
2018-03-01T00:00+01:00[CET]
2018-04-01T00:00+02:00[CET]
2018-05-01T00:00+02:00[CET]

Unfortunately, java.time has divided ISO8601 durations in 2 classes, where Period works with date-based fields, while Duration works with time-based fields.

Alternative

If you don't mind adding a dependency to your application, you can use the threeten extra lib: http://www.threeten.org/threeten-extra/

It contains the class PeriodDuration, that encapsulates both a Period and a Duration, so both "P1M" or "PT1H" will work:

// this works
PeriodDuration period = PeriodDuration.parse("P1M");

// this too
PeriodDuration period = PeriodDuration.parse("PT1H");

And the plus method can receive a PeriodDuration, because it also implements TemporalAmount.

答案 1 :(得分:0)

尝试使用do while循环。

ZonedDateTime startTimestamp = ZonedDateTime.of(LocalDateTime.of(2018, 4, 20, 12, 10), ZoneId.of("CET"));
ZonedDateTime endTimestamp = startTimestamp.plusHours(5);

Duration duration = Duration.parse("PT1H");
do {
    System.out.println(startTimestamp.toLocalTime());
    startTimestamp = endTimestamp.plus(duration);
} while (startTimestamp.isBefore(endTimestamp));

您必须重新初始化startTimeStamp,并将持续时间添加到结束时间戳。