From 4203e90655a3836fb564f32b6f5c978289e87d60 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Wed, 26 May 2021 16:45:52 +0200 Subject: [PATCH] Fix CronExpression roll-forward issue This commit makes sure that BitsCronField rolls the date forward in cases where we cannot find a next bit to elapse to. Closes: gh-26964 --- .../scheduling/support/BitsCronField.java | 7 +++++- .../support/CronExpressionTests.java | 23 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/support/BitsCronField.java b/spring-context/src/main/java/org/springframework/scheduling/support/BitsCronField.java index 947e321761..8ea07d5ba8 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/support/BitsCronField.java +++ b/spring-context/src/main/java/org/springframework/scheduling/support/BitsCronField.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -192,6 +192,11 @@ final class BitsCronField extends CronField { while (current != next && count++ < CronExpression.MAX_ATTEMPTS) { temporal = type().elapseUntil(temporal, next); current = type().get(temporal); + next = nextSetBit(current); + if (next == -1) { + temporal = type().rollForward(temporal); + next = nextSetBit(0); + } } if (count >= CronExpression.MAX_ATTEMPTS) { return null; diff --git a/spring-context/src/test/java/org/springframework/scheduling/support/CronExpressionTests.java b/spring-context/src/test/java/org/springframework/scheduling/support/CronExpressionTests.java index b4457c9e09..23319d5ca9 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/support/CronExpressionTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/support/CronExpressionTests.java @@ -497,6 +497,29 @@ class CronExpressionTests { assertThat(actual.getDayOfMonth()).isEqualTo(13); } + @Test + public void everyTenDays() { + CronExpression cronExpression = CronExpression.parse("0 15 12 */10 1-8 5"); + + LocalDateTime last = LocalDateTime.parse("2021-04-30T12:14:59"); + LocalDateTime expected = LocalDateTime.parse("2021-05-21T12:15"); + LocalDateTime actual = cronExpression.next(last); + assertThat(actual).isNotNull(); + assertThat(actual).isEqualTo(expected); + + last = actual; + expected = LocalDateTime.parse("2021-06-11T12:15"); + actual = cronExpression.next(last); + assertThat(actual).isNotNull(); + assertThat(actual).isEqualTo(expected); + + last = actual; + expected = LocalDateTime.parse("2022-01-21T12:15"); + actual = cronExpression.next(last); + assertThat(actual).isNotNull(); + assertThat(actual).isEqualTo(expected); + } + @Test void yearly() { CronExpression expression = CronExpression.parse("@yearly");