-
Notifications
You must be signed in to change notification settings - Fork 269
Open
Description
I'm using this in my Android Kotlin app and I noticed this bug.
Based on the javadoc comment describing the Everyfield expression, I expected this to work. I tried with a custom built cron using Every as described, but I got the same result.
Library version used: 9.2.1
Expectation: should return next execution date correctly. Should return execution dates for date range correctly.
Actual: only returns execution dates for the starting month each year.
import com.cronutils.model.CronType
import com.cronutils.model.definition.CronDefinitionBuilder
import com.cronutils.model.time.ExecutionTime
import com.cronutils.parser.CronParser
import org.junit.Assert.assertEquals
import org.junit.Test
import java.time.LocalDate
import java.time.LocalTime
import java.time.ZoneId
import java.time.ZonedDateTime
import kotlin.jvm.optionals.getOrNull
class MonthlyCronTest {
private val cronParser = CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ))
private fun LocalDate.toZoned(): ZonedDateTime =
ZonedDateTime.of(this, LocalTime.MIDNIGHT, ZoneId.systemDefault())
@Test
fun next_execution_for_month_interval() {
// Cron expression for "every 3 months starting in October"
val cron = cronParser.parse("0 0 0 * 10/3 ? *")
val executionTime = ExecutionTime.forCron(cron)
// Reference date: February 1, 2026
val referenceDate = LocalDate.of(2026, 2, 1).toZoned()
// Expected result: April 1, 2026
val expected = LocalDate.of(2026, 4, 1)
// Actual result: October 1, 2026
val actual = executionTime.nextExecution(referenceDate).getOrNull()?.toLocalDate()
// This assertion fails
assertEquals(expected, actual)
}
@Test
fun next_execution_for_month_interval_with_specific_day_of_month() {
// Cron expression for "15th of the month, every 3 months starting in October"
val cron = cronParser.parse("0 0 0 15 10/3 ? *")
val executionTime = ExecutionTime.forCron(cron)
// Reference date: January 1, 2026
val referenceDate = LocalDate.of(2026, 1, 1).toZoned()
// Expected result: January 15, 2026
val expected = LocalDate.of(2026, 1, 15)
// Actual result: October 15, 2026
val actual = executionTime.nextExecution(referenceDate).getOrNull()?.toLocalDate()
// This assertion fails
assertEquals(expected, actual)
}
@Test
fun execution_dates_for_month_interval() {
// Cron expression for "15th of the month, every 3 months starting in October"
val cron = cronParser.parse("0 0 0 15 10/3 ? *")
val executionTime = ExecutionTime.forCron(cron)
// Date range of Oct 1, 2025 to Oct 31, 2026
val startDate = LocalDate.of(2025, 10, 1).toZoned()
val endDate = LocalDate.of(2026, 10, 31).toZoned()
// Expected result:
// - Oct 15, 2025
// - Jan 15, 2026
// - Apr 15, 2026
// - Jul 15, 2026
// - Oct 15, 2026
val expected = listOf(
LocalDate.of(2025, 10, 15).toZoned(),
LocalDate.of(2026, 1, 15).toZoned(),
LocalDate.of(2026, 4, 15).toZoned(),
LocalDate.of(2026, 7, 15).toZoned(),
LocalDate.of(2026, 10, 15).toZoned()
)
// Actual result:
// - Oct 15, 2025
// - Oct 15, 2026
val actual = executionTime.getExecutionDates(startDate, endDate)
// This assertion fails
assertEquals(expected, actual)
}
}
Metadata
Metadata
Assignees
Labels
No labels