Files
skills/docs/foundation/timing/cron/src/fields/cron-fields.md
2026-05-29 08:20:56 +01:00

3.5 KiB

parseField

(no submodules)

Purpose

parseField is the single-field tokenizer at the heart of cron parsing. Each of the five (or six/seven) whitespace-separated positions in a cron expression is independently expanded into the sorted, unique numeric set of values it matches. The function is total over its FieldKind discriminator: it accepts the canonical crontab syntax surface (literals, ranges, lists, steps, wildcards, and alphabetic aliases for month + day-of-week) and rejects every Quartz extension (L/W/#/?) with typed errors so the caller can branch on err.code.

Features

  • Wildcards (*), literals, comma-separated lists, hyphenated ranges, step values (*/n and a-b/n and a/n), case-insensitive aliases (JAN-DEC for month, SUN-SAT for day-of-week).
  • Day-of-week normalization: both 0 and 7 are accepted for Sunday and collapse to 0 after sorting and deduplication.
  • Range bounds per field exposed via rangeFor(kind) for downstream next-run computations.
  • Typed errors via CronError: out-of-range numerics, unsupported tokens, and malformed atoms each surface a distinct CronErrorCode.

Usage: Basic Example

import { parseField } from "@suaveplan/cron";

const minutes = parseField("*/15", "minute");
console.log(Array.from(minutes)); // [0, 15, 30, 45]

const weekdays = parseField("MON-FRI", "dayOfWeek");
console.log(Array.from(weekdays)); // [1, 2, 3, 4, 5]

Usage: Advanced Example

import { parseField, rangeFor, CronError, CRON_CODES } from "@suaveplan/cron";

function expandOrFallback(token: string, kind: Parameters<typeof parseField>[1]) {
    try {
        return parseField(token, kind);
    } catch (err) {
        if (err instanceof CronError && err.code === CRON_CODES.UNSUPPORTED_SYNTAX) {
            const range = rangeFor(kind);
            return Object.freeze(
                Array.from({ length: range.max - range.min + 1 }, (_, i) => range.min + i)
            );
        }
        throw err;
    }
}

console.log(expandOrFallback("L", "dayOfMonth")); // falls back to full range

API Reference

  • parseField(token: string, kind: FieldKind): readonly number[] — returns a frozen, sorted, deduplicated value set. Throws CronError with codes CRON.UNSUPPORTED_SYNTAX, CRON.FIELD_OUT_OF_RANGE, or CRON.INVALID_EXPRESSION per the failure mode.
  • rangeFor(kind: FieldKind): FieldRange — inclusive {min, max} for the given field kind.
  • FieldKind — "second" | "minute" | "hour" | "dayOfMonth" | "month" | "dayOfWeek" | "year".
  • FieldRange — { readonly min: number; readonly max: number }.
  • FieldAliases — Readonly<Record<string, number>> (internal alias map shape).

Implementation Notes

The tokenizer walks comma-separated terms left-to-right, dispatching each term through the step parser before falling through to the range parser. Step extraction is bounded to a single / per term; nested steps (*/15/2) are rejected up front. Range expansion is O(n) over the result set and feeds a Set for deduplication before a final monotonically sorted Array.from. Day-of-week values are normalized after expansion so both 0,7 and SAT,SUN collapse correctly. Unsupported Quartz tokens (L/W/#/?) are caught by a single regex before any further parsing to keep the error path cheap; alias resolution is case-insensitive via toUpperCase. The output array is Object.freezed to prevent downstream mutation.