or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

date-operations.mdexpression-parsing.mdfield-system.mdfile-parsing.mdindex.mdschedule-iteration.md
tile.json

schedule-iteration.mddocs/

Schedule Iteration

Iteration capabilities for finding next/previous execution times, checking date ranges, and iterating through scheduled times with comprehensive date matching and boundary checking.

Capabilities

Next Execution Time

Get the next scheduled execution time based on the cron expression.

/**
 * Find the next scheduled date based on the cron expression
 * @returns The next scheduled date
 * @throws Error if no next date within constraints or loop limit exceeded
 */
next(): CronDate;

Usage Examples:

import { CronExpressionParser } from "cron-parser";

const hourly = CronExpressionParser.parse('0 * * * *');
const nextExecution = hourly.next();
console.log('Next hourly run:', nextExecution.toString());

// With timezone
const londonTime = CronExpressionParser.parse('0 9 * * 1-5', {
  tz: 'Europe/London'
});
console.log('Next London 9 AM weekday:', londonTime.next().toString());

// Multiple calls advance the iterator
const daily = CronExpressionParser.parse('@daily');
console.log('Tomorrow:', daily.next().toString());
console.log('Day after tomorrow:', daily.next().toString());

Previous Execution Time

Get the previous scheduled execution time based on the cron expression.

/**
 * Find the previous scheduled date based on the cron expression
 * @returns The previous scheduled date
 * @throws Error if no previous date within constraints or loop limit exceeded
 */
prev(): CronDate;

Usage Examples:

const weekly = CronExpressionParser.parse('@weekly');
const lastExecution = weekly.prev();
console.log('Last weekly run:', lastExecution.toString());

// Navigate backwards through time
const monthly = CronExpressionParser.parse('@monthly');
console.log('Last month:', monthly.prev().toString());
console.log('Month before that:', monthly.prev().toString());

Availability Checking

Check if next or previous scheduled times exist within constraints.

/**
 * Check if there is a next scheduled date within constraints
 * @returns True if next date exists, false otherwise
 */
hasNext(): boolean;

/**
 * Check if there is a previous scheduled date within constraints  
 * @returns True if previous date exists, false otherwise
 */
hasPrev(): boolean;

Usage Examples:

// Check availability before calling next/prev
const limited = CronExpressionParser.parse('0 0 * * *', {
  currentDate: '2023-12-01',
  endDate: '2023-12-31'
});

if (limited.hasNext()) {
  console.log('Next execution:', limited.next().toString());
} else {
  console.log('No more executions within date range');
}

// Safe iteration
while (limited.hasNext()) {
  const nextDate = limited.next();
  console.log('Scheduled:', nextDate.toString());
  
  // Break after reasonable number to avoid infinite loops
  if (someCondition) break;
}

Batch Date Retrieval

Get multiple next or previous dates in a single call.

/**
 * Get multiple scheduled dates
 * @param limit - Number of dates to retrieve (positive for next, negative for previous)
 * @returns Array of scheduled dates
 */
take(limit: number): CronDate[];

Usage Examples:

const every15min = CronExpressionParser.parse('*/15 * * * *');

// Get next 5 executions
const next5 = every15min.take(5);
console.log('Next 5 executions:');
next5.forEach((date, i) => {
  console.log(`${i + 1}: ${date.toString()}`);
});

// Get previous 3 executions (negative limit)
const prev3 = every15min.take(-3);
console.log('Previous 3 executions:');
prev3.forEach((date, i) => {
  console.log(`${i + 1}: ${date.toString()}`);
});

// Safe batch retrieval (stops if no more dates available)
const dailyLimited = CronExpressionParser.parse('@daily', {
  currentDate: '2023-12-28',
  endDate: '2023-12-31'
});
const remaining = dailyLimited.take(10); // Will only return available dates
console.log(`Got ${remaining.length} dates instead of requested 10`);

Iterator Reset

Reset the iterator position to a new date or the original starting date.

/**
 * Reset the iterator's current date to a new date or the initial date
 * @param newDate - Optional new date to reset to. If not provided, resets to initial date
 */
reset(newDate?: Date | CronDate): void;

Usage Examples:

const hourly = CronExpressionParser.parse('0 * * * *', {
  currentDate: '2023-01-01T00:00:00Z'
});

// Advance the iterator
console.log('First next:', hourly.next().toString());
console.log('Second next:', hourly.next().toString());

// Reset to initial date
hourly.reset();
console.log('After reset:', hourly.next().toString()); // Same as first next

// Reset to specific date
hourly.reset(new Date('2023-06-01T12:00:00Z'));
console.log('From June 1st:', hourly.next().toString());

// Reset with CronDate (preserving timezone)
const londonDate = new CronDate('2023-01-01T00:00:00Z', 'Europe/London');
hourly.reset(londonDate);
console.log('From London time:', hourly.next().toString());

Date Matching

Check if a specific date matches the cron expression.

/**
 * Check if the cron expression includes the given date
 * @param date - Date to check against the expression
 * @returns True if date matches the cron expression, false otherwise
 */
includesDate(date: Date | CronDate): boolean;

Usage Examples:

const workdayMorning = CronExpressionParser.parse('0 9 * * 1-5'); // 9 AM weekdays

// Check specific dates
const monday9am = new Date('2023-01-02T09:00:00Z'); // Monday
const saturday9am = new Date('2023-01-07T09:00:00Z'); // Saturday
const monday10am = new Date('2023-01-02T10:00:00Z'); // Monday but 10 AM

console.log('Monday 9 AM matches:', workdayMorning.includesDate(monday9am)); // true
console.log('Saturday 9 AM matches:', workdayMorning.includesDate(saturday9am)); // false  
console.log('Monday 10 AM matches:', workdayMorning.includesDate(monday10am)); // false

// Use with CronDate for timezone awareness
const nycTime = new CronDate('2023-01-02T14:00:00Z', 'America/New_York'); // 9 AM in NYC
console.log('NYC time matches:', workdayMorning.includesDate(nycTime));

// Practical usage: filtering dates
const dates = [
  new Date('2023-01-02T09:00:00Z'), // Monday 9 AM
  new Date('2023-01-03T09:00:00Z'), // Tuesday 9 AM  
  new Date('2023-01-07T09:00:00Z'), // Saturday 9 AM
];

const workdayDates = dates.filter(date => workdayMorning.includesDate(date));
console.log(`${workdayDates.length} dates match workday pattern`);

ES6 Iterator Support

Use ES6 iterator protocol for modern iteration patterns.

/**
 * Returns an ES6 iterator for iterating through future CronDate instances
 * @returns Iterator object for CronExpression that yields CronDate values
 */
[Symbol.iterator](): Iterator<CronDate>;

Usage Examples:

const every30min = CronExpressionParser.parse('*/30 * * * *');

// Use with for...of (be careful with infinite sequences!)
let count = 0;
for (const date of every30min) {
  console.log(`Execution ${count + 1}:`, date.toString());
  
  // Always include a break condition to avoid infinite loops
  if (++count >= 5) break;
}

// Use with iterator methods
const iterator = every30min[Symbol.iterator]();
console.log('First:', iterator.next().value.toString());
console.log('Second:', iterator.next().value.toString());

// Use with modern JavaScript features
const next10 = Array.from(
  { length: 10 }, 
  () => every30min[Symbol.iterator]().next().value
);
console.log('Next 10 executions:', next10.map(d => d.toString()));

// Practical example: collect until end date
const limitedIterator = CronExpressionParser.parse('0 */6 * * *', {
  currentDate: '2023-01-01',
  endDate: '2023-01-02'
});

const allDates = [];
for (const date of limitedIterator) {
  allDates.push(date);
  // Iterator will naturally stop when hasNext() returns false
}
console.log(`Collected ${allDates.length} dates within range`);

String Representation

Convert expression back to cron string format.

/**
 * Generate a string representation of the cron expression
 * @param includeSeconds - Whether to include the seconds field in output
 * @returns String representation of the cron expression
 */
stringify(includeSeconds?: boolean): string;

/**
 * Returns the string representation of the cron expression
 * Uses original expression if available, otherwise generates from fields
 * @returns The cron expression string
 */
toString(): string;

Usage Examples:

const expr = CronExpressionParser.parse('*/15 30 9-17 * * 1-5');

// Generate cron string without seconds (5-field format)
console.log('5-field format:', expr.stringify()); // "30 9-17 * * 1-5"

// Generate cron string with seconds (6-field format)  
console.log('6-field format:', expr.stringify(true)); // "*/15 30 9-17 * * 1-5"

// toString() method (uses original or generates)
console.log('toString():', expr.toString()); // Original expression or generated

// Roundtrip test
const original = '0 */10 8-18 * * MON-FRI';
const parsed = CronExpressionParser.parse(original);
const regenerated = parsed.stringify(true);

console.log('Original:', original);
console.log('Regenerated:', regenerated);
// Note: Regenerated may differ (e.g., MON-FRI becomes 1-5) but is functionally equivalent

Time Range Constraints

Working with expressions that have start/end date constraints.

// Time range constraints are set via CronExpressionOptions
interface TimeRangeOptions {
  startDate?: Date | string | number | CronDate;
  endDate?: Date | string | number | CronDate;
  currentDate?: Date | string | number | CronDate;
}

// Error thrown when iteration goes outside time span
const TIME_SPAN_OUT_OF_BOUNDS_ERROR_MESSAGE = 'Out of the time span range';

// Error thrown when iteration loop limit is exceeded (safety mechanism)
const LOOPS_LIMIT_EXCEEDED_ERROR_MESSAGE = 'Invalid expression, loop limit exceeded';

Usage Examples:

// Create expression with time constraints
const constrained = CronExpressionParser.parse('0 0 * * *', {
  startDate: '2023-01-01',
  endDate: '2023-01-31',
  currentDate: '2023-01-15'
});

try {
  // This will work - within range
  console.log('Next in range:', constrained.next().toString());
  
  // Keep getting next dates until we hit the boundary
  const allDates = constrained.take(50); // May return fewer than 50
  console.log(`Got ${allDates.length} dates within range`);
  
} catch (error) {
  if (error.message === TIME_SPAN_OUT_OF_BOUNDS_ERROR_MESSAGE) {
    console.log('Reached end of allowed time span');
  } else if (error.message === LOOPS_LIMIT_EXCEEDED_ERROR_MESSAGE) {
    console.log('Invalid expression: iteration loop limit exceeded (safety mechanism)');
  }
}

// Check boundaries
console.log('Has next:', constrained.hasNext());
console.log('Has prev:', constrained.hasPrev());

// Reset to test boundaries
constrained.reset(new Date('2023-01-31T23:59:59'));
console.log('At end, has next:', constrained.hasNext()); // false

constrained.reset(new Date('2023-01-01T00:00:01'));  
console.log('At start, has prev:', constrained.hasPrev()); // false