Skip to content

Null-safety migration + DOM/DOW correctness fixes - #10

Open
justintout wants to merge 2 commits into
masterfrom
fix/null-safety-and-dom-dow
Open

Null-safety migration + DOM/DOW correctness fixes#10
justintout wants to merge 2 commits into
masterfrom
fix/null-safety-and-dom-dow

Conversation

@justintout

Copy link
Copy Markdown
Owner

Migrates the package to sound null safety (Dart 3) and fixes the day-of-month / day-of-week matching bugs reported by several users.

Issues addressed

Root cause of the DOM/DOW bug

Two defects combined:

  1. The POSIX day rule was not implemented. matches() always ANDed the
    day-of-month and day-of-week fields (dayOfMonthMatches && dayOfWeekMatches).
    POSIX cron treats these two fields specially: when both are restricted
    (neither is *), a time matches if either field matches; when only one
    is restricted, only that field applies. Because the code always used AND, the
    both-restricted case was wrong — this is the "cron bug" that Test "Cron matchers day of week we pass a test for 'the bug'" is failing #8's test was
    meant to cover.

  2. dayOfMonthMatches evaluated the wrong field, and range parsing threw.
    The day-of-month matcher's skip (/) branch split _dayOfWeekField instead
    of _dayOfMonthField, and the hour/day-of-month/month/day-of-week matchers
    iterated a range (a-b) without a continue, then fell through to
    int.parse(value) on the range string. The net effect was that a restricted
    day-of-month such as the 7 in 0 19 7 8 * was effectively ignored, so the
    forward search stopped on the 1st of the month (2021-08-01) instead of the
    7th (2021-08-07). Likewise 0 0 1 * * / @monthly matched "tomorrow"
    rather than the 1st of the following month.

What changed

  • pubspec.yaml: SDK constraint >=2.7.0 <3.0.0^3.0.0; test bumped to
    ^1.24.0; version 0.1.10.2.0 (breaking change under 0.x semantics).
  • lib/src/cronparse.dart: migrated to sound null safety (late final for
    fields initialized in the constructor). The five per-field matchers now
    delegate to one shared _fieldMatches helper that correctly handles
    asterisks, skips, exact values, ranges, and comma-separated sets — fixing the
    wrong-field and range fall-through bugs at their root. matches() now applies
    the POSIX OR-vs-AND day rule. Public API surface is unchanged.
  • test/: added null-safety type annotations/casts; replaced the malformed
    * * *,* * SUN "the bug" expression with the valid * * 13 * SUN; added a
    regressions group with tests taken verbatim from issues Cron Next & Next Relative To set the next day.  #3, Scheduling cron for day of month / weekday does not work #4, and Day parameter seems buggy, e. g. 0 19 7 8 * returns 2021-08-01 19:00:00 #5
    (exact expressions and expected values).
  • Ran dart format (80-column default) across the repository.

Verification

  • dart analyze — no issues found.
  • dart test — all 26 tests pass, including the new regression tests.
  • dart format --set-exit-if-changed . — clean.

🤖 Generated with Claude Code

justintout and others added 2 commits July 14, 2026 13:10
Bump the SDK constraint to ^3.0.0 and make the library null-safe so it
can be consumed by modern Dart 3 / Flutter. Private fields set once in the
constructor become `late final`; test fixtures and helpers gain the type
annotations sound null safety requires.

Bump version 0.1.1 -> 0.2.0 (breaking change under 0.x semantics).

Fixes #6

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fix the day-of-month / day-of-week matching so results follow POSIX cron:

- The single field evaluator now honors a restricted day-of-month. The old
  `dayOfMonthMatches` iterated over the day-of-week field in its skip branch
  and the hour/DOM/month/DOW range branches fell through to `int.parse` on a
  range string, so a restricted day like `7` in `0 19 7 8 *` was effectively
  ignored and the search returned the 1st of the month instead of the 7th.
- `matches` now applies the POSIX day rule: when BOTH day-of-month and
  day-of-week are restricted, a time matches if EITHER matches; when only one
  is restricted, only that field applies. This is why `0 0 1 * *` / @monthly
  returned "tomorrow" instead of the 1st of the following month.

Replace the malformed `* * *,* * SUN` "the bug" test expression with the
valid `* * 13 * SUN`, and add regression tests taken directly from the issue
reports.

Fixes #3
Fixes #4
Fixes #5
Fixes #8

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant