像这样的调整器next(LocalTime time)
只对具有日期和时间的类型有意义。
Java 的 Time API 有 4 种类型:LocalDateTime
、OffsetDateTime
、ZonedDateTime
和Instant
.
为了完全支持所有 4,代码需要特殊处理Instant
, 因为Instant
和LocalTime
不直接相关,以及ZonedDateTime
, 处理 DST 重叠。
这个实现可以处理所有这些:
public static TemporalAdjuster next(LocalTime time) {
return temporal -> {
if (temporal instanceof Instant) {
OffsetDateTime utcDateTime = ((Instant) temporal).atOffset(ZoneOffset.UTC);
return next(utcDateTime, time).toInstant();
}
return next(temporal, time);
};
}
@SuppressWarnings("unchecked")
private static <T extends Temporal> T next(T refDateTime, LocalTime targetTime) {
T adjusted = (T) refDateTime.with(targetTime);
if (refDateTime.until(adjusted, ChronoUnit.NANOS) > 0)
return adjusted;
if (adjusted instanceof ChronoZonedDateTime<?>) {
ChronoZonedDateTime<?> laterOffset = ((ChronoZonedDateTime<?>) adjusted).withLaterOffsetAtOverlap();
if (laterOffset != adjusted && refDateTime.until(laterOffset, ChronoUnit.NANOS) > 0)
return (T) laterOffset;
}
return (T) refDateTime.plus(1, ChronoUnit.DAYS).with(targetTime);
}
测试(now()
在 2020 年 9 月 18 日上午 10 点之后的某个时间)
System.out.println(LocalDateTime.now().with(next(LocalTime.of(10, 0))));
System.out.println(OffsetDateTime.now().with(next(LocalTime.of(10, 0))));
System.out.println(ZonedDateTime.now().with(next(LocalTime.of(10, 0))));
System.out.println(Instant.now().with(next(LocalTime.of(10, 0))));
输出
2020-09-19T10:00
2020-09-19T10:00-04:00
2020-09-19T10:00-04:00[America/New_York]
2020-09-19T10:00:00Z
测试重叠
对于美国东部时区,DST 于 2020 年 11 月 1 日星期日凌晨 2:00 结束。
// We start at 1:45 AM EDT on November 1, 2020
ZoneId usEastern = ZoneId.of("America/New_York");
ZonedDateTime earlierOffset = ZonedDateTime.of(2020, 11, 1, 1, 45, 0, 0, usEastern);
System.out.println(earlierOffset);
// Now we look for next 1:20 AM after the 1:45 AM, and will find 1:20 AM EST
System.out.println(earlierOffset.with(next(LocalTime.of(1, 20))));
输出
2020-11-01T01:45-04:00[America/New_York]
2020-11-01T01:20-05:00[America/New_York]
尽管一天中的时间出现得更早(1:20 < 1:45),但它实际上是更晚的时间。