Skip to content

CronExpression skips days on midnight DST gap#36865

Merged
bclozel merged 2 commits into
spring-projects:7.0.xfrom
zmovo:fix-cron-dst-midnight-gap-36859
Jun 4, 2026
Merged

CronExpression skips days on midnight DST gap#36865
bclozel merged 2 commits into
spring-projects:7.0.xfrom
zmovo:fix-cron-dst-midnight-gap-36859

Conversation

@zmovo
Copy link
Copy Markdown

@zmovo zmovo commented Jun 3, 2026

Fixes #36859.

By default, BitsCronField#nextOrSame delegates to nextSetBit(0) after rollForward, which assumes the field value in the new period starts at zero. When daylight saving creates a gap at the start of a period (for example, Africa/Cairo advancing from 00:00 to 01:00), the rolled-forward temporal lands on a non-zero field value and matching from zero can advance an entire period too far, causing CronExpression#next to skip the calendar day.

This PR searches for the next matching bit from the actual field value in the new period instead, falling back to zero only when no bit matches in that period.

This does not change the existing contract for cron times that fall inside a DST gap: those executions are still skipped rather than deferred or replayed after the transition. The fix only ensures that subsequent valid matches on the same calendar day are still calculated correctly.

It also adds a regression test in CronExpressionTests#daylightSaving for a every-two-hours expression across the Cairo spring-forward boundary.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jun 3, 2026
@zmovo zmovo force-pushed the fix-cron-dst-midnight-gap-36859 branch from c52c11f to ceb9cd9 Compare June 3, 2026 07:28
@bclozel bclozel added type: bug A general bug in: core Issues in core modules (aop, beans, core, context, expression) and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Jun 3, 2026
@bclozel bclozel added this to the 7.0.x milestone Jun 3, 2026
@bclozel bclozel changed the title Fix CronExpression day skip on midnight DST gap CronExpression skips days on midnight DST gap Jun 3, 2026
@zmovo

This comment was marked as outdated.

@sbrannen

This comment was marked as outdated.

@bclozel bclozel self-assigned this Jun 4, 2026
@bclozel bclozel changed the base branch from main to 7.0.x June 4, 2026 07:39
bclozel pushed a commit to zmovo/spring-framework that referenced this pull request Jun 4, 2026
After rollForward, BitsCronField always searched for the next
matching bit from zero. When daylight saving creates a gap at
the start of a period (e.g. Africa/Cairo), the temporal lands on
a non-zero field value and matching from zero could advance an
entire period too far, skipping the calendar day.

Search from the actual field value in the new period instead,
falling back to zero only when no bit matches in that period.

Closes spring-projectsgh-36865

Signed-off-by: arno <me@zmovo.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@bclozel bclozel force-pushed the fix-cron-dst-midnight-gap-36859 branch from ceb9cd9 to a6dc70f Compare June 4, 2026 07:41
arno and others added 2 commits June 4, 2026 10:22
After rollForward, BitsCronField always searched for the next
matching bit from zero. When daylight saving creates a gap at
the start of a period (e.g. Africa/Cairo), the temporal lands on
a non-zero field value and matching from zero could advance an
entire period too far, skipping the calendar day.

Search from the actual field value in the new period instead,
falling back to zero only when no bit matches in that period.

See spring-projectsgh-36865

Signed-off-by: arno <me@zmovo.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
This fixes a potential regression introduced by the previous commit.
Because the current value was not updated after the temporal was rolled
forward, there were new cases where entire days would be skipped.

Closes spring-projectsgh-36865
@bclozel bclozel force-pushed the fix-cron-dst-midnight-gap-36859 branch from a6dc70f to 6467fca Compare June 4, 2026 08:22
@bclozel bclozel modified the milestones: 7.0.x, 7.0.8 Jun 4, 2026
bclozel pushed a commit that referenced this pull request Jun 4, 2026
After rollForward, BitsCronField always searched for the next
matching bit from zero. When daylight saving creates a gap at
the start of a period (e.g. Africa/Cairo), the temporal lands on
a non-zero field value and matching from zero could advance an
entire period too far, skipping the calendar day.

Search from the actual field value in the new period instead,
falling back to zero only when no bit matches in that period.

See gh-36865

Signed-off-by: arno <me@zmovo.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
bclozel added a commit that referenced this pull request Jun 4, 2026
This fixes a potential regression introduced by the previous commit.
Because the current value was not updated after the temporal was rolled
forward, there were new cases where entire days would be skipped.

Closes gh-36865
@bclozel bclozel merged commit 6467fca into spring-projects:7.0.x Jun 4, 2026
1 check failed
@github-actions github-actions Bot added the status: backported An issue that has been backported to maintenance branches label Jun 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

in: core Issues in core modules (aop, beans, core, context, expression) status: backported An issue that has been backported to maintenance branches type: bug A general bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The time of daylight saving time parsed by CronExpression is completely skipped

4 participants