DEV Community

toolzip
toolzip

Posted on Originally published at toolzip.app

Cron Expressions Demystified — Build a Human-Readable Cron Parser

Cron Expressions Demystified — Build a Human-Readable Cron Parser

Cron expressions are one of those things that look scary until you break them down. Here's how to parse them and generate human-readable descriptions in JavaScript.

The Five Fields

┌───── minute (0–59)
│ ┌───── hour (0–23)
│ │ ┌───── day of month (1–31)
│ │ │ ┌───── month (1–12)
│ │ │ │ ┌───── day of week (0–7, 0 and 7 = Sunday)
│ │ │ │ │
* * * * *
Enter fullscreen mode Exit fullscreen mode

Special Characters

  • * — every value
  • */n — every n-th value
  • n-m — range from n to m
  • n,m — specific values n and m

Parsing a Field

function parseField(value, min, max, names = []) {
  if (value === '*') return 'every';

  if (value.startsWith('*/')) {
    const step = parseInt(value.slice(2));
    return `every ${step}`;
  }

  if (value.includes('-')) {
    const [start, end] = value.split('-').map(Number);
    const startName = names[start] ?? start;
    const endName = names[end] ?? end;
    return `${startName} through ${endName}`;
  }

  if (value.includes(',')) {
    const parts = value.split(',').map(v => names[parseInt(v)] ?? v);
    return parts.join(', ');
  }

  return names[parseInt(value)] ?? value;
}
Enter fullscreen mode Exit fullscreen mode

Generating Human-Readable Output

const MONTHS = ['','January','February','March','April','May','June',
  'July','August','September','October','November','December'];
const DAYS = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];

function describe(cron) {
  const [min, hour, dom, month, dow] = cron.split(' ');
  const parts = [];

  if (min === '0' && hour !== '*') {
    parts.push(`at ${hour}:00`);
  } else if (min !== '*') {
    parts.push(`at minute ${min}`);
  }

  if (hour !== '*' && min !== '0') {
    parts.push(`past hour ${hour}`);
  }

  if (dow !== '*') {
    parts.push(`on ${parseField(dow, 0, 7, DAYS)}`);
  }

  if (dom !== '*') {
    parts.push(`on day ${dom} of the month`);
  }

  if (month !== '*') {
    parts.push(`in ${parseField(month, 1, 12, MONTHS)}`);
  }

  return parts.length ? parts.join(', ') : 'every minute';
}
Enter fullscreen mode Exit fullscreen mode

Common Patterns

const PRESETS = [
  { label: 'Every minute',           cron: '* * * * *' },
  { label: 'Every hour',             cron: '0 * * * *' },
  { label: 'Every day at midnight',  cron: '0 0 * * *' },
  { label: 'Every Monday at 9am',    cron: '0 9 * * 1' },
  { label: 'First of every month',   cron: '0 0 1 * *' },
  { label: 'Weekdays at 9am',        cron: '0 9 * * 1-5' },
];
Enter fullscreen mode Exit fullscreen mode

Try it at toolzip.app/tools/cron-builder.

Top comments (0)