aksh1618
aksh1618

Reputation: 2571

How do I get a Java Duration from a cron expression?

I have a scheduled task in a spring boot application:

@Scheduled(fixedRateString = "${scheduled.task.rate}")
public void runScheduledTask() {
    // ...
}

With a corresponding test:

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

Now I want to use a cron instead of a fixed rate:

@Scheduled(cron = "${scheduled.task.cron}")

Now I need to adapt the test to this. How do get a Duration object corresponding to the frequency of the cron expression?

Upvotes: 5

Views: 3539

Answers (1)

aksh1618
aksh1618

Reputation: 2571

Spring specific solution:

Spring provides a CronExpression class which can be used to parse a cron expression and get the next Temporal instance at which it will be triggered after the provided Temporal.

So to get a Duration:

CronExpression cronExpression = CronExpression.parse(scheduledTaskCron);
LocalDateTime nextExecution = cronExpression.next(LocalDateTime.now());
LocalDateTime nextToNextExecution = cronExpression.next(nextExecution);
Duration durationBetweenExecutions = Duration.between(
        nextExecution, nextToNextExecution
);

Note that Instant can't be used as the Temporal here, as it can't represent all the components of a cron expression, such as Day of Week, giving the following exception:

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)

Original answer for spring versions < 5.3

Spring provides a CronSequenceGenerator which can be used to parse a cron expression and get the next Date instance at which it will be triggered after the provided Date.

So to get a Duration:

CronSequenceGenerator generator = new CronSequenceGenerator(scheduledTaskCron);
Date nextExecution = generator.next(new Date());
Date nextToNextExecution = generator.next(nextExecution);
Duration durationBetweenExecutions = Duration.between(
        nextExecution.toInstant(), nextToNextExecution.toInstant()
);

Upvotes: 10

Related Questions