npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@didrod2539/croniq

v0.2.0

Published

Tiny, zero-dependency cron toolkit: parse cron expressions, compute next/previous run times (field-jumping, fast), validate, and describe them in plain English. Works in Node, Deno, Bun and the browser.

Readme

croniq

Parse cron expressions, compute the next/previous run times, and describe them in plain English — in ~2 KB, with zero dependencies.

npm version bundle size CI types license

"When does 0 9 * * 1-5 run next?" is a question you should never answer by hand — and definitely shouldn't trust an LLM to guess. It's exact calendar arithmetic (ranges, steps, month lengths, leap years, the day-of-month/day-of-week union rule). croniq does it deterministically, and tells you in English.

import { parse } from "@didrod2539/croniq";

const cron = parse("0 9 * * 1-5");      // 09:00, weekdays
cron.next();                            // → Date of the next weekday 09:00
cron.describe();                        // "At 09:00, Monday through Friday"
cron.matches(new Date());               // is right now a run time?

Why croniq?

  • 🎯 Correct, not approximate. Leap days, month lengths, and the Vixie-cron day-of-month OR day-of-week rule are all handled. Verified against known schedules in tests.
  • Fast. Field-jumping (not minute-by-minute scanning), so sparse schedules like 0 0 29 2 * resolve in microseconds.
  • 🗣️ Human-readable. describe() turns an expression into a sentence — great for dashboards and confirmations.
  • 🧩 Full syntax. 5 fields or 6 (with seconds), ranges 1-5, lists 1,3,5, steps */15, names JAN/MON, ?, and macros @daily/@hourly/@weekly/…
  • 🕒 UTC or local. One flag.
  • 🪶 ~2 KB gzipped, zero dependencies. Node 18+, Deno, Bun, Workers and the browser.

Install

npm install @didrod2539/croniq
# or: pnpm add @didrod2539/croniq  /  yarn add @didrod2539/croniq

Published under the @didrod2539 npm scope (the unscoped name croniq was blocked by npm for being too close to cron). The import name matches the package name; everything else is identical.

Ships ESM and CommonJS:

import { parse, isValid } from "@didrod2539/croniq";        // ESM / TypeScript
const { parse, isValid } = require("@didrod2539/croniq");   // CommonJS

CLI

npx @didrod2539/croniq "0 9 * * 1"             # describe + the next runs
npx @didrod2539/croniq describe "*/15 * * * *" # "Every 15 minutes"
npx @didrod2539/croniq next "0 0 1 * *" --iso -n 5
npx @didrod2539/croniq valid "61 * * * *"      # exit 1 if invalid

The bundled command is croniq. Commands: describe, next, prev, valid. Options: -n/--count, --utc, --iso. valid sets the exit code.

Usage

Next & previous runs

const cron = parse("*/15 * * * *");       // every 15 minutes

cron.next();                              // next run after now
cron.next(new Date("2026-03-15T12:07Z")); // → 2026-03-15T12:15:00Z
cron.prev();                              // previous run before now
cron.nextN(5);                            // next five runs (Date[])

Validate

isValid("0 9 * * 1-5"); // true
isValid("99 * * * *");  // false

Describe

parse("0 9 * * 1-5").describe();   // "At 09:00, Monday through Friday"
parse("*/15 * * * *").describe();  // "Every 15 minutes"
parse("@hourly").describe();       // "At 0 minutes past every hour"
parse("0 0 1 1 *").describe();     // "At 00:00, on day-of-month 1, in January"

UTC vs local

parse("0 0 * * *", { utc: true }).next();  // midnights in UTC
parse("0 0 * * *").next();                 // midnights in the host's local time

Seconds field

A 6-field expression adds a leading seconds column:

parse("*/30 * * * * *").next(); // every 30 seconds

Supported syntax

| Field | Values | Allowed | | ------------- | ----------------- | ----------------------------- | | Second (opt.) | 0–59 | * , - / | | Minute | 0–59 | * , - / | | Hour | 0–23 | * , - / | | Day of month | 1–31 | * , - / ? | | Month | 1–12 or JANDEC | * , - / | | Day of week | 0–7 or SUNSAT (0 & 7 = Sunday) | * , - / ? |

Macros: @yearly / @annually, @monthly, @weekly, @daily / @midnight, @hourly. When both day-of-month and day-of-week are restricted, a date matches if either matches (standard cron behavior).

L, W, and # modifiers aren't supported yet — open an issue if you need them.

API

| Member | Description | | ------ | ----------- | | parse(expr, opts?) | Parse into a Cron (throws on invalid input). | | isValid(expr, opts?) | true/false without throwing. | | cron.next(from?) / cron.prev(from?) | Next/previous run Date. | | cron.nextN(n, from?) / cron.prevN(n, from?) | Arrays of run times. | | cron.matches(date?) | Does the date satisfy the expression? | | cron.describe() | Plain-English description. | | cron.minutes / .hours / .daysOfMonth / .months / .daysOfWeek / .seconds | Parsed value arrays. |

Comparison

| | croniq | hand-rolled date math | heavier cron libs | | ------------------------ | :------: | :-------------------: | :---------------: | | next / prev computation | ✅ | ⚠️ | ✅ | | Plain-English describe | ✅ | ❌ | ⚠️ (separate lib) | | Validation | ✅ | ❌ | ✅ | | Zero dependencies | ✅ | ✅ | ⚠️ | | ~2 KB gzipped | ✅ | — | ❌ |

Contributing

Contributions are very welcome! Please read CONTRIBUTING.md and our Code of Conduct.

git clone https://github.com/didrod205/croniq.git
cd croniq
npm install
npm test

💖 Sponsor

croniq is free and MIT-licensed, built and maintained in spare time. If it saved you from debugging a misfiring schedule, please consider supporting it — every bit helps keep the project healthy.

  • Star this repo — the simplest, free way to help others discover it.
  • 🍋 Sponsor via Lemon Squeezy — one-time or recurring support.

Sponsoring? Open an issue and we'll add your name/logo here. Thank you! 🙏

License

MIT © croniq contributors