pipr.tools

cron-explain

Explain cron expressions in plain English

⇔ Decode

Try it

stdin0 chars
stdout0 chars

Example

Explain cron schedules in English

Usage
$ echo "*/15 9-17 * * 1-5
0 0 * * 0
30 2 1 * *" | cron-explain
View source
(input) => {
      const MONTHS = [
        ,
        "Jan",
        "Feb",
        "Mar",
        "Apr",
        "May",
        "Jun",
        "Jul",
        "Aug",
        "Sep",
        "Oct",
        "Nov",
        "Dec",
      ];
      const DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];

      const explain = (expr) => {
        const parts = expr.trim().split(/\s+/);
        if (parts.length < 5)
          return `Error: need 5 fields, got ${parts.length}`;
        const [min, hour, dom, mon, dow] = parts;
        const bits = [];

        // Minute
        if (min === "*") {
          bits.push("Every minute");
        } else if (min.startsWith("*/")) {
          bits.push(`Every ${min.slice(2)} minutes`);
        } else {
          bits.push(`At minute ${min}`);
        }

        // Hour
        if (hour !== "*") {
          if (hour.startsWith("*/")) {
            bits.push(`every ${hour.slice(2)} hours`);
          } else if (hour.includes("-")) {
            const [a, b] = hour.split("-");
            bits.push(`${a}:00\u2013${b}:00`);
          } else if (hour.includes(",")) {
            bits.push(
              `at ${hour
                .split(",")
                .map((h) => h + ":00")
                .join(", ")}`,
            );
          } else if (!min.startsWith("*/") && min !== "*") {
            bits[0] = `At ${hour.padStart(2, "0")}:${min.padStart(2, "0")}`;
          } else {
            bits.push(`during hour ${hour}`);
          }
        }

        // Day of month
        if (dom !== "*") {
          if (dom.startsWith("*/")) bits.push(`every ${dom.slice(2)} days`);
          else bits.push(`on day ${dom} of the month`);
        }

        // Month
        if (mon !== "*") {
          const resolve = (v) => {
            const n = parseInt(v);
            return isNaN(n) ? v : MONTHS[n] || v;
          };
          if (mon.includes("-")) {
            const [a, b] = mon.split("-");
            bits.push(`in ${resolve(a)}\u2013${resolve(b)}`);
          } else {
            bits.push(`in ${mon.split(",").map(resolve).join(", ")}`);
          }
        }

        // Day of week
        if (dow !== "*") {
          const resolve = (v) => {
            const n = parseInt(v);
            return isNaN(n) ? v : DAYS[n] || v;
          };
          if (dow.includes("-")) {
            const [a, b] = dow.split("-");
            bits.push(`on ${resolve(a)}\u2013${resolve(b)}`);
          } else {
            bits.push(`on ${dow.split(",").map(resolve).join(", ")}`);
          }
        }

        return bits.join(", ");
      };

      return input
        .trim()
        .split("\n")
        .map((line) => {
          line = line.trim();
          if (!line) return "";
          return `${line}  \u2192  ${explain(line)}`;
        })
        .join("\n");
    }

Suggested Pipelines

Related Tools