Convert cron expressions into human readable descriptions
npx @tessl/cli install tessl/npm-cronstrue@3.2.0Cronstrue is a JavaScript library that parses cron expressions and outputs human-readable descriptions of cron schedules. It supports all cron expression special characters, 5-7 part expressions, Quartz Job Scheduler compatibility, and internationalization with 39 languages.
npm install cronstrueimport cronstrue from "cronstrue";For CommonJS:
const cronstrue = require("cronstrue");For full internationalization support:
import cronstrue from "cronstrue/i18n";const cronstrue = require("cronstrue/i18n");For individual locale loading:
import cronstrue from "cronstrue";
import "cronstrue/locales/fr";
import "cronstrue/locales/es";import cronstrue from "cronstrue";
// Basic cron expression conversion
console.log(cronstrue.toString("* * * * *"));
// Output: "Every minute"
console.log(cronstrue.toString("0 23 ? * MON-FRI"));
// Output: "At 11:00 PM, Monday through Friday"
// With options
console.log(cronstrue.toString("0 23 * * *", { verbose: true }));
// Output: "At 11:00 PM, every day"
console.log(cronstrue.toString("23 14 * * SUN#2", { use24HourTimeFormat: true }));
// Output: "At 14:23, on the second Sunday of the month"
// With localization
console.log(cronstrue.toString("*/5 * * * *", { locale: "fr" })); // requires i18n import
// Output: "Toutes les 5 minutes"Cronstrue is built around several key components:
The library follows a parse-then-describe pattern where expressions are first validated and parsed into components, then each component is processed through locale-specific description generators.
Converts cron expressions to human-readable descriptions with extensive customization options.
/**
* Converts a cron expression into a description a human can read
* @param expression - The cron expression
* @param options - Optional configuration object
* @returns Human readable description
*/
function toString(
expression: string,
options?: Options
): string;
interface Options {
/** If exception occurs when trying to parse expression, whether to throw or return error message (Default: true) */
throwExceptionOnParseError?: boolean;
/** Whether to use a verbose description (Default: false) */
verbose?: boolean;
/** Whether to interpret cron expression DOW 1 as Sunday or Monday (Default: true) */
dayOfWeekStartIndexZero?: boolean;
/** Whether to interpret January as 0 or 1 (Default: false) */
monthStartIndexZero?: boolean;
/** If true, descriptions will use 24-hour clock (Default: false, varies by locale) */
use24HourTimeFormat?: boolean;
/** The locale to use (Default: "en") */
locale?: string | null;
/** @deprecated Timezone offset in minutes - no longer supported */
tzOffset?: number;
}Main class for advanced usage and custom initialization.
class ExpressionDescriptor {
/** Available locale objects */
static locales: { [name: string]: Locale };
/** Default locale identifier */
static defaultLocale: string;
/** Special cron characters: ["/", "-", ",", "*"] */
static specialCharacters: string[];
/** The cron expression being processed */
expression: string;
/** Parsed components of the cron expression */
expressionParts: string[];
/** Configuration options */
options: Options;
/** Locale object for translations */
i18n: Locale;
/**
* Creates new ExpressionDescriptor instance
* @param expression - Cron expression
* @param options - Configuration options
*/
constructor(expression: string, options: Options);
/**
* Converts a cron expression into a description a human can read
* @param expression - The cron expression
* @param options - Optional configuration object
* @returns Human readable description
*/
static toString(expression: string, options?: Options): string;
/**
* Initializes the ExpressionDescriptor with locale data
* @param localesLoader - Loader for locale data
* @param defaultLocale - Default locale (defaults to "en")
*/
static initialize(localesLoader: LocaleLoader, defaultLocale?: string): void;
/**
* Gets the full human-readable description (protected method)
* @protected
*/
protected getFullDescription(): string;
}Core parsing engine that handles expression validation and component extraction.
/**
* Parses and validates cron expressions into component parts
*/
class CronParser {
/** The cron expression being parsed */
expression: string;
/** Whether DOW 1 represents Sunday (true) or Monday (false) */
dayOfWeekStartIndexZero: boolean;
/** Whether January is represented as 0 (true) or 1 (false) */
monthStartIndexZero: boolean;
/**
* Creates a new CronParser instance
* @param expression - Cron expression to parse
* @param dayOfWeekStartIndexZero - DOW interpretation (default: true)
* @param monthStartIndexZero - Month interpretation (default: false)
*/
constructor(expression: string, dayOfWeekStartIndexZero?: boolean, monthStartIndexZero?: boolean);
/**
* Parses the cron expression into component parts
* @returns Array of parsed expression parts [seconds, minutes, hours, dayOfMonth, month, dayOfWeek, year]
*/
parse(): string[];
/**
* Handles special cron expressions like @yearly, @monthly, etc.
* @param expression - Special expression to parse
* @returns Standard cron expression equivalent
*/
parseSpecial(expression: string): string;
}Validation utilities for ensuring cron expression components are within valid ranges.
/**
* Validates cron expression component ranges
*/
class RangeValidator {
/** Validates seconds range (0-59) */
static secondRange(parse: string): void;
/** Validates minutes range (0-59) */
static minuteRange(parse: string): void;
/** Validates hours range (0-23) */
static hourRange(parse: string): void;
/** Validates day of month range (1-31) */
static dayOfMonthRange(parse: string): void;
/** Validates month range (1-12 or 0-11 based on monthStartIndexZero) */
static monthRange(parse: string, monthStartIndexZero: boolean): void;
/** Validates day of week range (0-7 or 1-7 based on dayOfWeekStartIndexZero) */
static dayOfWeekRange(parse: string, dayOfWeekStartIndexZero: boolean): void;
}Complete localization support with 39 available languages.
interface LocaleLoader {
/** Loads locale data into provided object */
load(availableLocales: { [name: string]: Locale }): void;
}
interface Locale {
/** Default time format for locale */
use24HourTimeFormatByDefault(): boolean;
/** Error message when expression parsing fails */
anErrorOccuredWhenGeneratingTheExpressionD(): string;
/** Basic time translations */
everyMinute(): string;
everyHour(): string;
everySecond(): string;
atSpace(): string;
at(): string;
spaceAnd(): string;
/** AM/PM indicators (optional) */
pm?(): string;
am?(): string;
/** @reboot translation (optional) */
atReboot?(): string;
/** Controls whether AM/PM appears before or after time (optional) */
setPeriodBeforeTime?(): boolean;
/** Custom verbosity replacements for concise mode (optional) */
conciseVerbosityReplacements?(): Record<string, string>;
/** Days of week with grammatical case support (optional) */
daysOfTheWeekInCase?(caseForm?: number): string[];
/** Months with grammatical case support (optional) */
monthsOfTheYearInCase?(caseForm?: number): string[];
/** Template strings for intervals */
everyX0Seconds(s?: string): string;
everyX0Minutes(s?: string): string;
everyX0Hours(s?: string): string;
everyX0Days(s?: string): string;
everyX0Months(s?: string): string;
everyX0Years(s?: string): string;
/** Date and time component arrays */
daysOfTheWeek(): string[];
monthsOfTheYear(): string[];
/** Advanced grammar support */
daysOfTheWeekInCase?(f?: number): string[];
monthsOfTheYearInCase?(f?: number): string[];
/** Text replacements for concise mode */
conciseVerbosityReplacements?(): Record<string, string>;
/** Additional translation methods for complex expressions */
everyMinuteBetweenX0AndX1(): string;
secondsX0ThroughX1PastTheMinute(): string;
atX0SecondsPastTheMinute(s?: string): string;
atX0SecondsPastTheMinuteGt20(): string | null;
minutesX0ThroughX1PastTheHour(): string;
atX0MinutesPastTheHour(s?: string): string;
atX0MinutesPastTheHourGt20(): string | null;
betweenX0AndX1(): string;
atX0(): string;
commaEveryDay(): string;
commaEveryX0DaysOfTheWeek(s?: string): string;
commaX0ThroughX1(s?: string): string;
commaAndX0ThroughX1(s?: string): string;
commaMonthX0ThroughMonthX1(): string | null;
commaYearX0ThroughYearX1(): string | null;
first(s?: string): string;
second(s?: string): string;
third(s?: string): string;
fourth(s?: string): string;
fifth(s?: string): string;
commaOnThe(s?: string, day?: string): string;
spaceX0OfTheMonth(): string;
lastDay(): string;
commaOnTheLastX0OfTheMonth(s?: string): string;
commaOnlyOnX0(s?: string): string;
commaAndOnX0(): string;
commaEveryX0Months(s?: string): string;
commaOnlyInX0(): string;
commaOnlyInMonthX0?(): string;
commaOnlyInYearX0?(): string;
commaOnTheLastDayOfTheMonth(): string;
commaOnTheLastWeekdayOfTheMonth(): string;
commaDaysBeforeTheLastDayOfTheMonth(s?: string): string;
firstWeekday(): string;
weekdayNearestDayX0(): string;
commaOnTheX0OfTheMonth(): string;
commaEveryX0Days(s?: string): string;
commaBetweenDayX0AndX1OfTheMonth(s?: string): string;
commaOnDayX0OfTheMonth(s?: string): string;
commaEveryX0Years(s?: string): string;
commaStartingX0(): string;
dayX0?(): string;
setPeriodBeforeTime?(): boolean;
}Pre-built locale loaders for different use cases.
/** Loads English locale only - lightweight option */
class enLocaleLoader implements LocaleLoader {
load(availableLocales: { [name: string]: Locale }): void;
}
/** Loads all 39 available locales - full internationalization */
class allLocalesLoader implements LocaleLoader {
load(availableLocales: { [name: string]: Locale }): void;
}Direct access to individual locale implementations for advanced usage.
// Individual locale classes exported from cronstrue/i18n/allLocales
export {
en, af, ar, be, bg, ca, cs, da, de, es, fa, fi, fr, he, hr, hu,
id, it, ja, ko, my, nb, nl, pl, pt_BR, pt_PT, ro, ru, sk, sl,
sv, sw, th, tr, uk, vi, zh_CN, zh_TW
} from "cronstrue/i18n/allLocales";
// Usage example
import { fr, de, es } from "cronstrue/i18n/allLocales";
import cronstrue from "cronstrue";
// Initialize with specific locales
cronstrue.ExpressionDescriptor.initialize({
load: (locales) => {
locales['fr'] = new fr();
locales['de'] = new de();
locales['es'] = new es();
}
});String manipulation utilities used internally but available for advanced usage.
class StringUtilities {
/**
* Replaces %s placeholders in template with provided values
* @param template - Template string with %s placeholders
* @param values - Replacement values
* @returns Formatted string
*/
static format(template: string, ...values: string[]): string;
/**
* Checks if text contains any of the search strings
* @param text - Text to search in
* @param searchStrings - Array of strings to search for
* @returns True if any search string is found
*/
static containsAny(text: string, searchStrings: string[]): boolean;
}minute hour day-of-month month day-of-weeksecond minute hour day-of-month month day-of-week OR minute hour day-of-month month day-of-week yearsecond minute hour day-of-month month day-of-week year* - Wildcard (any value)/ - Step values (e.g., */5 = every 5), - Lists (e.g., 1,3,5)- - Ranges (e.g., 1-5)? - No specific value (day-of-month/day-of-week)L - Last (e.g., last day of month)W - Weekday nearest (e.g., 15W)# - Nth occurrence (e.g., SUN#2 = 2nd Sunday)@yearly / @annually - Once a year (0 0 1 1 *)@monthly - Once a month (0 0 1 * *)@weekly - Once a week (0 0 * * 0)@daily / @midnight - Once a day (0 0 * * *)@hourly - Every hour (0 * * * *)@reboot - At startup (special case)39 supported languages: en (English), af (Afrikaans), ar (Arabic), be (Belarusian), bg (Bulgarian), ca (Catalan), cs (Czech), da (Danish), de (German), es (Spanish), fa (Farsi), fi (Finnish), fr (French), he (Hebrew), hr (Croatian), hu (Hungarian), id (Indonesian), it (Italian), ja (Japanese), ko (Korean), my (Malay), nb (Norwegian), nl (Dutch), pl (Polish), pt_BR (Portuguese Brazil), pt_PT (Portuguese Portugal), ro (Romanian), ru (Russian), sk (Slovakian), sl (Slovenian), sv (Swedish), sw (Swahili), th (Thai), tr (Turkish), uk (Ukrainian), vi (Vietnamese), zh_CN (Chinese Simplified), zh_TW (Chinese Traditional)
When installed globally, cronstrue provides a CLI interface supporting multiple argument formats:
# Install globally
npm install -g cronstrue
# Single quoted expression
cronstrue "*/5 * * * *"
# Output: Every 5 minutes
cronstrue "@daily"
# Output: At 12:00 AM, every day
# 5 arguments: minute hour day-of-month month day-of-week
cronstrue 1 2 3 4 5
# Output: At 02:01 AM, on day 3 of the month, and on Friday, only in April
# 6 arguments: second minute hour day-of-month month day-of-week
cronstrue 0 1 2 3 4 5
# Output: At 02:01:00 AM, on day 3 of the month, and on Friday, only in April
# 7 arguments: second minute hour day-of-month month day-of-week year
cronstrue 0 1 2 3 4 5 2025
# Output: At 02:01:00 AM, on day 3 of the month, and on Friday, only in April, only in 2025
# Error handling for incorrect argument count
cronstrue 1 2 3
# Output: Error: too few arguments (3): 1 2 3
# Usage (5 args): cronstrue minute hour day-of-month month day-of-week
# or
# Usage (6 args): cronstrue second minute hour day-of-month month day-of-week
# or
# Usage (7 args): cronstrue second minute hour day-of-month month day-of-week year<script src="https://unpkg.com/cronstrue@latest/dist/cronstrue.min.js"></script>
<script>
var cronstrue = window.cronstrue;
console.log(cronstrue.toString("*/5 * * * *"));
</script><script src="https://unpkg.com/cronstrue@latest/dist/cronstrue-i18n.min.js"></script>
<script>
var cronstrue = window.cronstrue;
console.log(cronstrue.toString("*/5 * * * *", { locale: "fr" }));
</script><script src="https://unpkg.com/cronstrue@latest/dist/cronstrue.min.js"></script>
<script src="https://unpkg.com/cronstrue@latest/locales/fr.min.js"></script>
<script src="https://unpkg.com/cronstrue@latest/locales/es.min.js"></script>
<script>
var cronstrue = window.cronstrue;
console.log(cronstrue.toString("*/5 * * * *", { locale: "fr" }));
</script>For advanced usage, you can import components directly:
// Direct component imports
import { ExpressionDescriptor } from "cronstrue/dist/expressionDescriptor";
import { CronParser } from "cronstrue/dist/cronParser";
import { StringUtilities } from "cronstrue/dist/stringUtilities";
import { RangeValidator } from "cronstrue/dist/rangeValidator";
// Individual locale imports
import { en, fr, de } from "cronstrue/i18n/allLocales";
// Direct locale loader imports
import { enLocaleLoader } from "cronstrue/i18n/enLocaleLoader";
import { allLocalesLoader } from "cronstrue/i18n/allLocalesLoader";
// Advanced usage example
const parser = new CronParser("*/5 * * * *", true, false);
const parts = parser.parse();
console.log(parts); // ["*", "*/5", "*", "*", "*"]
// Custom validation
try {
RangeValidator.minuteRange("*/75"); // Invalid - minutes only go 0-59
} catch (error) {
console.error("Invalid minute range");
}import cronstrue from "cronstrue";
// Default: throws exception on invalid expression
try {
cronstrue.toString("invalid expression");
} catch (error) {
console.error(error); // Throws parsing error
}
// Alternative: return error message
const result = cronstrue.toString("invalid expression", {
throwExceptionOnParseError: false
});
console.log(result); // Returns locale-specific error message// Automatic validation occurs during parsing
try {
cronstrue.toString("75 * * * *"); // Invalid minute (>59)
} catch (error) {
console.error("Minute values must be between 0 and 59");
}
try {
cronstrue.toString("* 25 * * *"); // Invalid hour (>23)
} catch (error) {
console.error("Hour values must be between 0 and 23");
}// If specified locale doesn't exist, falls back to first available locale
const description = cronstrue.toString("* * * * *", { locale: "nonexistent" });
// Console warning: "Locale 'nonexistent' could not be found; falling back to 'en'."
// Deprecated tzOffset warning
const result = cronstrue.toString("* * * * *", { tzOffset: 300 });
// Console warning: "'tzOffset' option has been deprecated and is no longer supported."