cron-parser

repository·master·Indexed 23 days ago

https://github.com/harrisiirak/cron-parser

A Node.js library (v5.6.2) for parsing and manipulating cron expressions. It supports standard cron formats with optional second fields, predefined aliases (e.g., @yearly, @daily), and advanced features including timezone support via Luxon, DST handling, and randomized 'H' (jitter) values. The library provides CronExpressionParser for calculating future and past occurrences, CronFileParser for crontab files, and a CronDate class for date manipulation.

Tokens
7.6K
Snippets
15
Records
37
Agent score
80%

What's inside cron-parser

  1. Understand the Cron Format and Special Characters

    master

    The library supports a standard cron format with an optional second field. The order of fields is:

    second (0-59, optional) | minute (0-59) | hour (0-23) | day of month (1-31) | month (1-12) | day of week (0-7, where 0 or 7 is Sunday).

    Special Characters

    CharacterDescriptionExample
    *Any value* * * * * (every minute)
    ?Any value (alias for *)? * * * * (every minute)
    ,Value list separator1,2,3 * * * * (1st, 2nd, and 3rd minute)
    -Range of values1-5 * * * * (every minute from 1 through 5)
    /Step values*/5 * * * * (every 5th minute)
    LLast day of month/week0 0 L * * (midnight on last day of month)
    #Nth day of month0 0 * * 1#1 (first Monday of month)
    HRandomized valueH * * * * (every n minute where n is randomly picked within [0, 59])
  2. How date range handling and automatic clamping works

    master

    The library manages date ranges automatically to simplify iteration:

    1. startDate as fallback: If currentDate is omitted but startDate is provided, startDate becomes the currentDate.
    2. Automatic clamping: If currentDate is outside the range defined by startDate and endDate, it is automatically adjusted (clamped) to the nearest bound.
    3. Validation during iteration: Even if currentDate is clamped, the library validates bounds during iteration. If .next() would result in a date exceeding endDate, it throws an error: "Out of the time span range".
    const options = {
      currentDate: '2022-01-01T00:00:00Z', // Before startDate
      startDate: '2023-01-01T00:00:00Z',
      endDate: '2024-01-01T00:00:00Z',
    };
    // currentDate will be clamped to startDate (2023-01-01T00:00:00Z) automatically
    const interval = CronExpressionParser.parse('0 0 * * *', options);
  3. Use Hash (H) support for jitter

    master

    To prevent load spikes in job scheduling, the library supports the H special character. When H is used instead of *, it is replaced by a randomized value within the valid range of that field. This is inspired by Jenkins's cron syntax.

    Supported patterns:

    • H: Random value in the field range.
    • H/step: Random offset followed by a step (e.g., H/5).
    • H(range): Random value within a specific range (e.g., H(0-10)).
    • H#n: Random value at the $n^{th}$ occurrence of a specific day (e.g., * * * * H#3).

    Seedable Randomness: You can make the jitter deterministic by providing a hashSeed in the CronExpressionOptions. Using the same seed will always produce the same randomized values.

    import { CronExpressionParser } from 'cron-parser';
    
    // At 23:<randomized> on every day-of-week from Monday through Friday.
    const interval = CronExpressionParser.parse('H 23 * * 1-5');
    
    // At <randomized>:30 everyday.
    const interval = CronExpressionParser.parse('30 H * * *');
    
    // At every minutes of <randomized> second everyday.
    const interval = CronExpressionParser.parse('H * H * * *');
    
    // At every 5th minute starting from a random offset.
    const interval = CronExpressionParser.parse('H/5 * * * *');
    
    // At a random minute within the range 0-10 everyday.
    const interval = CronExpressionParser.parse('H(0-10) * * * *');
    
    // At every 5th minute starting from a random offset within the range 0-4.
    const interval = CronExpressionParser.parse('H(0-29)/5 * * * *');
    
    // At every minute of the third <randomized> day of the month
    const interval = CronExpressionParser.parse('* * * * H#3');
    
    // Seedable randomness
    const options = {
      currentDate: '2023-03-26T01:00:00',
      hashSeed: 'main-backup',
    };
    
    const interval = CronExpressionParser.parse('H * * * H', options);
    console.log(interval.stringify()); // "12 * * * 4"
    
    const otherInterval = CronExpressionParser.parse('H * * * H', options);
    // Using the same seed will always return the same jitter
    console.log(otherInterval.stringify()); // "12 * * * 4"
  4. Use the 'L' character for Last Day of Month/Week

    master

    The library supports the L character in the weekday position to represent the "last occurrence of this weekday for the month in progress".

    • 0L - 7L: Represents the last occurrence of a specific weekday.
    • L in the day of month position: Represents the last day of the month.

    Example expressions:

    • 0 0 0 * * 1L: Last Monday of every month at midnight.
    • 0 0 0 * * 1,3L: Every Monday and the last Wednesday of the month.
    • 0 0 L * *: Last day of every month.
    import { CronExpressionParser } from 'cron-parser';
    
    // Last Monday of every month at midnight
    const lastMonday = CronExpressionParser.parse('0 0 0 * * 1L');
    
    // You can also combine L expressions with other weekday expressions
    // This will run every Monday and the last Wednesday of the month
    const mixedWeekdays = CronExpressionParser.parse('0 0 0 * * 1,3L');
    
    // Last day of every month
    const lastDay = CronExpressionParser.parse('0 0 L * *');
  5. Enable Strict Mode for cron expression validation

    master

    By default, the library allows setting both Day of Month and Day of Week simultaneously, which can lead to ambiguous behavior. Activating strict: true in the options object enforces the following validation rules:

    1. Day Of Month and Day Of Week: Prevents setting both fields at the same time.
    2. Complete Expression: Requires all 6 fields (second, minute, hour, day of month, month, day of week) to be present.
    3. Non-empty Expression: Rejects empty expressions that would otherwise default to '0 * * * * *'.

    Use this to ensure your cron expressions are unambiguous and correctly formatted.

    import { CronExpressionParser } from 'cron-parser';
    
    const options = {
      currentDate: new Date('Mon, 12 Sep 2022 14:00:00'),
      strict: true,
    };
    
    try {
      // This will throw an error in strict mode because it uses both dayOfMonth and dayOfWeek
      CronExpressionParser.parse('0 0 12 1-31 * 1', options);
    } catch (err) {
      console.log('Error:', err.message);
      // Error: Cannot use both dayOfMonth and dayOfWeek together in strict mode!
    }
  6. Configure CronExpressionParser with options

    master

    Pass an options object to CronExpressionParser.parse() to control timezone, date ranges, and validation.

    Options:

    • currentDate (Date | string | number): The starting point for iteration. Defaults to current local time in UTC. Supported string formats: ISO8601, HTTP/RFC2822, SQL.
    • endDate (Date | string | number): Sets the end point of the iteration range.
    • startDate (Date | string | number): Sets the start point of the iteration range. If currentDate is not provided, startDate is used as the fallback.
    • tz (string): Timezone (e.g., 'Europe/London').
    • hashSeed (string): A seed for the H (randomized) special character.
    • strict (boolean): Enable strict mode validation.
    import { CronExpressionParser } from 'cron-parser';
    
    const options = {
      currentDate: '2023-01-01T00:00:00Z',
      endDate: '2024-01-01T00:00:00Z',
      tz: 'Europe/London',
    };
    
    try {
      const interval = CronExpressionParser.parse('0 0 * * *', options);
      console.log('Next:', interval.next().toString());
    } catch (err) {
      console.log('Error:', err.message);
    }
  7. Modify cron fields programmatically

    master

    You can create a new cron expression by modifying specific fields of an existing one using CronFieldCollection.from. This method accepts either CronField instances or raw values (like arrays of numbers) for the fields you wish to change.

    This is useful for keeping most of an expression intact while updating specific parts like the hour or minute.

    import { CronExpressionParser, CronFieldCollection, CronHour, CronMinute } from 'cron-parser';
    
    // Parse original expression
    const interval = CronExpressionParser.parse('0 7 * * 1-5');
    
    // Create new collection with modified fields using raw values
    const modified = CronFieldCollection.from(interval.fields, {
      hour: [8],
      minute: [30],
      dayOfWeek: [1, 3, 5],
    });
    
    console.log(modified.stringify()); // "30 8 * * 1,3,5"
    
    // You can also use CronField instances
    const modified2 = CronFieldCollection.from(interval.fields, {
      hour: new CronHour([15]),
      minute: new CronMinute([30]),
    });
    
    console.log(modified2.stringify()); // "30 15 * * 1-5"
  8. Parse cron expressions with CronExpressionParser

    master

    Use CronExpressionParser.parse(expression, options?) to create an interval object. This object allows you to iterate through future or past dates.

    Methods:

    • .next(): Returns the next occurrence.
    • .prev(): Returns the previous occurrence.
    • .take(n): Returns an array of the next n occurrences.
    import { CronExpressionParser } from 'cron-parser';
    
    try {
      const interval = CronExpressionParser.parse('*/2 * * * *');
    
      // Get next date
      console.log('Next:', interval.next().toString());
      // Get next 3 dates
      console.log(
        'Next 3:',
        interval.take(3).map((date) => date.toString()),
      );
    
      // Get previous date
      console.log('Previous:', interval.prev().toString());
    } catch (err) {
      console.log('Error:', err.message);
    }
  9. Parse crontab files with CronFileParser

    master

    Use CronFileParser to parse standard crontab files. It supports both asynchronous and synchronous parsing.

    Result Object Fields:

    • variables: Extracted variables from the file.
    • expressions: Parsed cron expressions.
    • errors: Any errors encountered during parsing.
    import { CronFileParser } from 'cron-parser';
    
    // Async file parsing
    try {
      const result = await CronFileParser.parseFile('/path/to/crontab');
      console.log('Variables:', result.variables);
      console.log('Expressions:', result.expressions);
      console.log('Errors:', result.errors);
    } catch (err) {
      console.log('Error:', err.message);
    }
    
    // Sync file parsing
    try {
      const result = CronFileParser.parseFileSync('/path/to/crontab');
      console.log('Variables:', result.variables);
      console.log('Expressions:', result.expressions);
      console.log('Errors:', result.errors);
    } catch (err) {
      console.log('Error:', err.message);
    }
  10. Handle timezones and DST transitions

    master

    The library uses Luxon to provide robust timezone support, ensuring that Daylight Saving Time (DST) transitions are handled correctly. Pass the tz option with a valid timezone identifier (e.g., 'Europe/London') and provide a currentDate.

    import { CronExpressionParser } from 'cron-parser';
    
    const options = {
      currentDate: '2023-03-26T01:00:00',
      tz: 'Europe/London',
    };
    
    const interval = CronExpressionParser.parse('0 * * * *', options);
    
    // Will correctly handle DST transition
    console.log('Next dates during DST transition:');
    console.log(interval.next().toString());
    console.log(interval.next().toString());
    console.log(interval.next().toString());
  11. Iterate through cron intervals

    master

    The object returned by CronExpressionParser.parse() is an iterator. You can traverse scheduled dates using a for...of loop or use the .take(n) method to retrieve a specific number of upcoming dates.

    import { CronExpressionParser } from 'cron-parser';
    
    const interval = CronExpressionParser.parse('0 */2 * * *');
    
    // Using for...of
    for (const date of interval) {
      console.log('Iterator value:', date.toString());
      if (someCondition) break;
    }
    
    // Using take() for a specific number of iterations
    const nextFiveDates = interval.take(5);
    console.log(
      'Next 5 dates:',
      nextFiveDates.map((date) => date.toString()),
    );