如何解决查找下一次出现的时间,例如TemporalAdjuster
JSR-310中是否有任何东西可以查找给定时间的下一次出现?我确实在寻找与this question相同的东西,但只是时间而不是几天。
例如,从世界标准时间2020-09-17的世界标准时间06:30开始,我想查找下一次出现的05:30:
LocalTime time = LocalTime.of(5,30);
zoneddatetime startDateTime = zoneddatetime.of(2020,9,17,6,30,ZoneId.of("UTC"));
zoneddatetime nextTime = startDateTime.with(TemporalAdjusters.next(time)); // This doesn't exist
在上面,我想nextTime
代表UTC 2020-09-18在UTC时间05:30,即第二天早上05:30。
为了澄清我的期望,所有时间都是05:30:
----------------------------------------
| startDateTime | expected nextTime |
| 2020-09-07 06:30 | 2020-09-08 05:30 |
| 2020-09-07 05:00 | 2020-09-07 05:30 |
| 2020-09-07 05:30 | 2020-09-08 05:30 |
----------------------------------------
解决方法
如果您只希望它与LocalDateTime
和LocalTime
或一天工作24小时的任何其他类型的Temporal
一起使用,则逻辑很简单:
public static TemporalAdjuster nextTime(LocalTime time) {
return temporal -> {
LocalTime lt = LocalTime.from(temporal);
if (lt.isBefore(time)) {
return temporal.with(time);
} else {
return temporal.plus(Duration.ofHours(24)).with(time);
}
};
}
但是实际上要对所有具有时间成分的Temporal
执行此操作非常困难。考虑一下您对ZonedDateTime
要做的事情。而不是增加24小时,我们可能需要增加23或25小时,因为DST转换使“天”变短或变长。您可以通过添加“天”来解决此问题:
public static TemporalAdjuster nextTime(LocalTime time) {
return temporal -> {
LocalTime lt = LocalTime.from(temporal);
if (lt.isBefore(time) || !temporal.isSupported(ChronoUnit.DAYS)) {
return temporal.with(time);
} else {
return temporal.plus(1,ChronoUnit.DAYS).with(time);
}
};
}
但是,它仍然总是不能正确处理间隙和重叠。例如,当我们要求从01:31开始的下一个01:30,并且在02:00有1小时的重叠过渡,即时钟在02:00倒退了一个小时。正确的答案是增加59分钟,但是上面的代码将给我们第二天的日期时间。要处理这种情况,您需要做一些复杂的事情,例如Andreas的回答。
如果您查看其他内置的时间调整器,它们都非常简单,所以我想他们只是不想增加这种复杂性。
,像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年10月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,45,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
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。