Cron Seconds Support Implementation
Implementation of seconds support in cron expressions with six-field format, making every N seconds the default scheduling option
Cron Seconds Support Implementation Summary
Overview
Added support for seconds in cron expressions, making the default option “Every N seconds” with a default of 5 seconds.
Changes Made
1. CronDialog.kt - UI Form Updates
File: /composeApp/src/commonMain/kotlin/krill/zone/krillapp/executor/cron/CronDialog.kt
Added Variables:
everySeconds- default “5”dailySecond- default “0”weeklySecond- default “0”monthlySecond- default “0”- Changed default mode from
"every_minutes"to"every_seconds"
UI Components:
- Added “Every N seconds” radio button option above the minutes option
- Added seconds input field for daily, weekly, and monthly modes
- Updated all mode radio buttons to include seconds configuration
Expression Building:
Updated buildExpression() to generate 6-field cron expressions:
every_seconds:"*/N * * * * *"(default:"*/5 * * * * *")every_minutes:"0 */N * * * *"(prepends 0 for seconds)every_hours:"0 M */H * * *"daily:"S M H * * *"weekly:"S M H * * DOW"monthly:"S M H D * *"custom: User-defined expression
Expression Parsing:
Enhanced LaunchedEffect to handle both formats:
- 6-field format:
SEC MIN HOUR DOM MON DOW(primary) - 5-field format:
MIN HOUR DOM MON DOW(legacy, assumes second=0)
2. CronLogic.kt - Core Logic Updates
File: /krill-sdk/src/commonMain/kotlin/krill/zone/krillapp/trigger/cron/CronLogic.kt
CronSpec Data Class:
Added seconds: Set<Int> field as the first parameter
parseExpression():
- Now accepts both 5-field and 6-field formats
- 6-field:
SEC MIN HOUR DOM MON DOW - 5-field:
MIN HOUR DOM MON DOW(assumes seconds = 0)
nextExecution():
- Changed from searching by minute boundaries to second boundaries
- Added
secondfield matching in the execution logic - Loop increments by 1 second instead of 1 minute
- Checks seconds field first, then minutes, hours, etc.
Before:
1
2
3
4
fun nextExecution(cronSpec: CronSpec, now: Instant): Instant? {
var candidate = now.plus(1, DateTimeUnit.MINUTE) // Start at next minute
// ...loop by minutes
}
After:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fun nextExecution(cronSpec: CronSpec, now: Instant): Instant? {
var candidate = now.plus(1, DateTimeUnit.SECOND) // Start at next second
while (attempts < maxAttempts) {
val dt = candidate.toLocalDateTime(TimeZone.currentSystemDefault())
// Check seconds first
if (cronSpec.seconds.isNotEmpty() && dt.second !in cronSpec.seconds) {
candidate = candidate.plus(1, DateTimeUnit.SECOND)
attempts++
continue
}
// Then check minutes, hours, etc.
// ...
}
}
3. ServerCronProcessor.kt - Execution Updates
File: /krill-sdk/src/commonMain/kotlin/krill/zone/krillapp/executor/cron/ServerCronProcessor.kt
Continuous Loop:
Changed the execution loop to work with second-level precision:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private suspend fun executeCronJob(node: Node): Boolean {
while (true) {
val cronSpec = CronLogic.parseExpression(expression)
val nextTime = CronLogic.nextExecution(cronSpec, Clock.System.now())
if (nextTime == null) {
logger.e { "Failed to calculate next execution for ${node.name()}" }
return false
}
val waitMillis = nextTime.toEpochMilliseconds() - Clock.System.now().toEpochMilliseconds()
if (waitMillis > 0) {
delay(waitMillis)
}
// Execute children at scheduled time
nodeManager.executeChildren(node)
}
}
Backward Compatibility
The implementation maintains full backward compatibility with existing 5-field cron expressions:
- 5-field expressions are automatically converted to 6-field with seconds=0
- Existing cron nodes continue to work without modification
- UI gracefully handles both formats when loading existing nodes
Example conversions:
1
2
3
4
5
6
7
8
9
10
11
// 5-field input
"*/5 * * * *" // Every 5 minutes
// Parsed as 6-field
"0 */5 * * * *" // Every 5 minutes at 0 seconds
// 6-field input
"*/30 * * * * *" // Every 30 seconds
// Parsed as-is
"*/30 * * * * *"
UI Changes
New Default Screen
When creating a new cron node, users now see:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Schedule
● Every N seconds
Seconds: [5]
○ Every N minutes
Minutes: [5]
○ Every N hours
Hours: [1] Minute: [0]
○ Daily at time
Hour: [0] Minute: [0] Second: [0]
○ Weekly on days
[Days selector]
Hour: [0] Minute: [0] Second: [0]
○ Monthly on day
Day: [1] Hour: [0] Minute: [0] Second: [0]
○ Custom cron expression
[Text field]
Generated cron: */5 * * * * *
Field Validation
All numeric inputs are validated:
- Seconds: 0-59
- Minutes: 0-59
- Hours: 0-23
- Day of month: 1-31
Testing
Test Cases Verified:
- Every N seconds:
*/5 * * * * *- Every 5 seconds ✓*/30 * * * * *- Every 30 seconds ✓0,15,30,45 * * * * *- At 0, 15, 30, 45 seconds ✓
- Daily with seconds:
30 15 9 * * *- 9:15:30 AM daily ✓0 0 0 * * *- Midnight exactly ✓
- Weekly with seconds:
0 0 9 * * 1- 9:00:00 AM Mondays ✓30 30 14 * * 5- 2:30:30 PM Fridays ✓
- Backward compatibility:
*/5 * * * *→0 */5 * * * *✓0 9 * * *→0 0 9 * * *✓
Performance Considerations
Before (minute precision):
- Loop increments: 1 minute
- Max iterations: ~525,600 per year search
- Typical delay: 1-60 seconds
After (second precision):
- Loop increments: 1 second
- Max iterations: ~31,536,000 per year search
- Typical delay: 0-60 seconds
- Safeguard: maxAttempts = 366 * 24 * 60 * 60 (1 year of seconds)
The second-level precision increases computational overhead slightly, but the maxAttempts safeguard prevents infinite loops.
Migration Notes
For Users:
- Existing cron nodes work unchanged
- New cron nodes default to “Every 5 seconds”
- Can still use minute/hour/daily/weekly/monthly modes
- Custom expressions support both 5-field and 6-field formats
For Developers:
CronLogic.parseExpression()handles both formats automatically- UI components gracefully handle missing seconds field
- All processors work with both formats
Future Enhancements
Potential improvements:
- Add preset buttons (every 10s, 30s, 1m, 5m, etc.)
- Visual timeline showing next 5 executions
- Validation warnings for unrealistic schedules
- Expression tester with live preview
Related Documents
Conclusion
The seconds support implementation provides users with fine-grained scheduling control while maintaining full backward compatibility. The default of “Every 5 seconds” makes it easy to create responsive automation workflows for time-sensitive tasks like sensor polling or status monitoring.