如何从 cron 表达式获取 Java 持续时间?

aks*_*618 3 java cron spring java-time

我在 Spring Boot 应用程序中有一个计划任务:

@Scheduled(fixedRateString = "${scheduled.task.rate}")
public void runScheduledTask() {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

并进行相应的测试:

@Test
public void test_scheduledTask_runs() {
    await().atMost(Duration.ofMillis(scheduledTaskRate).multipliedBy(2)).untilAsserted(() -> {
        Mockito.verify(scheduledTasks, Mockito.atLeastOnce()).runScheduledTask();
    });
}
Run Code Online (Sandbox Code Playgroud)

现在我想使用 cron 而不是固定速率:

@Scheduled(cron = "${scheduled.task.cron}")
Run Code Online (Sandbox Code Playgroud)

现在我需要使测试适应这一点。如何获取Duration与cron表达式频率对应的对象?

aks*_*618 6

Spring具体解决方案:

Spring 提供了一个CronExpression类,可用于解析cron 表达式并获取在提供的 后触发该表达式的下一个 实例。TemporalTemporal

所以要得到一个Duration

CronExpression cronExpression = CronExpression.parse(scheduledTaskCron);
LocalDateTime nextExecution = cronExpression.next(LocalDateTime.now());
LocalDateTime nextToNextExecution = cronExpression.next(nextExecution);
Duration durationBetweenExecutions = Duration.between(
        nextExecution, nextToNextExecution
);
Run Code Online (Sandbox Code Playgroud)

请注意,Instant不能用作此处的Temporal,因为它不能表示 cron 表达式的所有组件,例如Day of Week,会出现以下异常:

java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: DayOfWeek
    at java.base/java.time.Instant.get(Instant.java:565)
    at org.springframework.scheduling.support.CronField$Type.get(CronField.java:200)
    at org.springframework.scheduling.support.BitsCronField.nextOrSame(BitsCronField.java:180)
    at org.springframework.scheduling.support.CronExpression.nextOrSameInternal(CronExpression.java:264)
    at org.springframework.scheduling.support.CronExpression.nextOrSame(CronExpression.java:252)
    at org.springframework.scheduling.support.CronExpression.next(CronExpression.java:245)
Run Code Online (Sandbox Code Playgroud)

spring 版本 < 5.3 的原始答案

Spring 提供了一个CronSequenceGenerator,可用于解析 cron 表达式并获取在提供的 后触发它的下一个 实例。DateDate

所以要得到一个Duration

java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: DayOfWeek
    at java.base/java.time.Instant.get(Instant.java:565)
    at org.springframework.scheduling.support.CronField$Type.get(CronField.java:200)
    at org.springframework.scheduling.support.BitsCronField.nextOrSame(BitsCronField.java:180)
    at org.springframework.scheduling.support.CronExpression.nextOrSameInternal(CronExpression.java:264)
    at org.springframework.scheduling.support.CronExpression.nextOrSame(CronExpression.java:252)
    at org.springframework.scheduling.support.CronExpression.next(CronExpression.java:245)
Run Code Online (Sandbox Code Playgroud)