查找下一次出现的时间,例如TemporalAdjuster

如何解决查找下一次出现的时间,例如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  |
----------------------------------------

解决方法

如果您只希望它与LocalDateTimeLocalTime或一天工作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种类型,例如:LocalDateTimeOffsetDateTimeZonedDateTimeInstant

要完全支持所有4种,由于InstantInstant不可直接建立关系,因此LocalTimeZonedDateTime都必须进行特殊处理,以处理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 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?