DEV Community

Aniket Saini
Aniket Saini

Posted on

Why I ripped out a cron parsing library and wrote my own

Building the cron builder for Quietbench, I started with an npm package for parsing and calculating next-run times. Get it working, move on. That lasted about a week.

The problem: the library handled standard cron fine, but broke on real edge cases — conditional OR/AND behavior when day-of-week and day-of-month are combined, step values inside ranges (1-10/2), and off-by-one errors around DST transitions. When I checked its output against standard cron engines, the mismatches showed up fast.

Why not patch it: the parsing and "next run time" logic were tightly coupled internally. Fixing one edge case risked breaking another. Writing something scoped to exactly what I needed was less work than fighting someone else's abstraction.

The rewrite keeps three things cleanly separate: tokenize/validate each field, normalize day-of-week/day-of-month interaction explicitly, and compute next executions by walking forward in time rather than solving algebraically. Slower in theory, irrelevant in practice — nobody notices a few milliseconds when they're reading 5 output times.

Building the cron builder for Quietbench, I started with an npm package for parsing and calculating next-run times. Get it working, move on. That lasted about a week.

The problem: the library handled standard cron fine, but broke on real edge cases — conditional OR/AND behavior when day-of-week and day-of-month are combined, step values inside ranges (1-10/2), and off-by-one errors around DST transitions. When I checked its output against standard cron engines, the mismatches showed up fast.

Why not patch it: the parsing and "next run time" logic were tightly coupled internally. Fixing one edge case risked breaking another. Writing something scoped to exactly what I needed was less work than fighting someone else's abstraction.

The rewrite keeps three things cleanly separate: tokenize/validate each field, normalize day-of-week/day-of-month interaction explicitly, and compute next executions by walking forward in time rather than solving algebraically. Slower in theory, irrelevant in practice — nobody notices a few milliseconds when they're reading 5 output times.

**The actual lesson: **reaching for a library first isn't wrong. But once I was spending more time reverse-engineering why the library disagreed with standard cron behavior than I would've spent writing the logic myself, that was the signal to just own it.

Live at quietbench.dev/cron-builder — curious if anyone finds an edge case it still gets wrong. reaching for a library first isn't wrong. But once I was spending more time reverse-engineering why the library disagreed with standard cron behavior than I would've spent writing the logic myself, that was the signal to just own it.

Live at quietbench.dev/cron-builder — curious if anyone finds an edge case it still gets wrong.

Top comments (0)