1

首先,“发生在午夜”的定义是当任务运行时,new DateTime()或类似的将在转换为人类可读格式时显示 00:00:00 或更晚的时间部分。重要的一点是它不能显示前一天的 23:59:59。

实现此目的的一种常见方法是计算从现在到所需时间点的毫秒数,然后使用 aScheduledExecutorService在正确的时间执行任务。但是,当插入闰秒时,这将导致任务提前一秒运行(或提前几毫秒,具体取决于闰秒的“涂抹”方式以及您安排任务的时间):

Runnable task = ...
long numberOfMillisUntilMidnight = ...
ScheduledExecutorService executor = ...

// task runs too early when leap seconds are inserted
executor.schedule(task, numberOfMillisUntilMidnight, TimeUnit.MILLISECONDS);

原因是executor.schedule()基于System.nanoTime()它显然忽略了闰秒。我想我需要的是一些基于“此时运行”而不是“在这段时间后运行”的调度程序。

对于那些感兴趣的人来说,任务必须在午夜运行的原因与我系统中的所有事件必须根据它们发生的日期分类这一事实有关,并且在可能的情况下,这需要与另一个系统。当然,如果另一个系统在每个事件上标上今天的日期会更好,但我们不在那里。

4

3 回答 3

1

我想我需要的是一些基于“此时运行”而不是“在这段时间后运行”的调度程序

那将是全唱歌,全跳舞的解决方案。但:

首先,“发生在午夜”的定义是当任务运行时,new DateTime() 或类似的时间部分将显示 00:00:00或更晚......重要的是它不能显示 23:59:前一天的59。

(我的重点)

简单的方法总是添加第二个,甚至两个。所以在普通情况下是 00:00:01 (00:00:00 或更晚) ,在闰秒情况下是00:00:00 (不是 23:59:59) 。

于 2017-01-04T10:36:55.803 回答
1

根据由此产生的讨论,似乎很清楚,一般来说,如果“墙上时间”对您很重要,那么依靠您的调度程序在“正确”时间运行任务是不明智的。在夏令时期间以相同的“墙上时间”运行日常任务时也是如此,尽管与闰秒情况不同,现有工具(例如 Quartz)很好地支持夏令时情况。

相反,我认为这种“时间敏感”进程的最佳方法是在任务运行时检查系统时钟。如果您的计划由于某种原因不准确(闰秒不是您的系统时钟相对于由 测量的经过时间进行调整的唯一时间System.nanoTime())并且尚未达到时间,那么什么也不做,并将任务重新安排到正确的时间。这种方法也适用于响应夏令时变化的时间表,但如上所述,这已经得到通用工具的支持。

这种方法受到上述 Jonathon Reinhart 评论的启发。重新安排而不是睡觉似乎更好。

于 2017-01-04T14:51:49.707 回答
0

假设您的具体ScheduledExecutorService实现依赖于System.nanoTime()(如您所说)并考虑到您的要求/配置,即方法schedule(...)的初始延迟参数计算直到下一个午夜经过的毫秒数,包括可能的闰秒,

我建议您使用闰秒感知解决方案。使用我的库Time4J的示例显示了如何计算延迟参数:

Moment now = SystemClock.currentMoment(); // should not be called during a leap second
Moment nextMidnight =
    now.with(
        PlainTime.COMPONENT.setToNext(PlainTime.midnightAtStartOfDay()).in(
            Timezone.ofSystem().with(
                GapResolver.NEXT_VALID_TIME.and(OverlapResolver.EARLIER_OFFSET)
            )
        )
    );
long delayInMilliseconds = SI.NANOSECONDS.between(now, nextMidnight) / 1_000_000;

在夏令时更改的情况下,此代码还将选择午夜之后最早的有效本地时间(标准 Java 宁愿将时间向前推进间隙的大小,可能导致本地时间晚于第一个有效时间)。对于大多数区域,这仅在选择午夜之后的任意开始时间时才相关(取决于业务需求)。

无论如何,您还应该关心将您的系统连接到同一个 NTP 时钟。您要么依赖 OS-NTP 配置,要么可以使用基于 Java 的时钟连接到 NTP(Time4J 在这里也提供了一个解决方案)。

如果您选择的时钟只是随意跳动(即,如果有人手动调整它或在 NTP 配置错误的情况下),那么在再次检查本地 walltime 后重新安排任务可能更安全。但是,我仍然认为通过上面的 Time4J 代码计算延迟参数是一个好主意,因为匹配午夜的机会比仅仅运行任务并重新检查本地时间要高。

您还可以结合这两种方法:精确计算延迟和检查/重新安排。

于 2017-01-04T11:21:36.923 回答