Tips > Building Workflows

Handle Date Math That Expressions Cannot

n8n's built-in expression language supports Luxon for dates, but complex operations like business day calculations, timezone-aware scheduling windows

For date math beyond what Luxon expressions handle -- business-day counts, timezone-aware scheduling windows, or fiscal-quarter logic -- use a Code node instead. It can hold reusable helper functions and loops that would take an unreasonable number of IF and DateTime nodes to replicate on the canvas. The example below finds the next business day at least 30 days out, skipping weekends and US federal holidays.

When should you use a Code node for date math?

n8n's built-in expression language supports Luxon for dates, but complex operations like business day calculations, timezone-aware scheduling windows, or fiscal quarter logic are easier and more maintainable in a Code node.

Real-world example: A contract renewal workflow needs to calculate the next business day that is at least 30 days out, skipping US federal holidays and weekends.

How do you calculate the next business day in n8n?

// Mode: Run Once for Each Item

// US federal holidays for the current year (update annually or fetch dynamically)
const holidays2025 = new Set([
  '2025-01-01', '2025-01-20', '2025-02-17', '2025-05-26',
  '2025-06-19', '2025-07-04', '2025-09-01', '2025-10-13',
  '2025-11-11', '2025-11-27', '2025-12-25',
]);

function isBusinessDay(date) {
  const day = date.getDay();
  if (day === 0 || day === 6) return false; // Weekend
  const iso = date.toISOString().slice(0, 10);
  return !holidays2025.has(iso);
}

function addBusinessDays(start, days) {
  const result = new Date(start);
  let added = 0;
  while (added < days) {
    result.setDate(result.getDate() + 1);
    if (isBusinessDay(result)) added++;
  }
  return result;
}

const contractEnd = new Date($input.item.json.contractEndDate);
const minDate = new Date(contractEnd);
minDate.setDate(minDate.getDate() + 30);

// Find next business day on or after the 30-day mark
let renewalDate = new Date(minDate);
while (!isBusinessDay(renewalDate)) {
  renewalDate.setDate(renewalDate.getDate() + 1);
}

// Also calculate 5-business-day reminder window
const reminderDate = addBusinessDays(renewalDate, -5);

return [{
  json: {
    ...$input.item.json,
    renewalDate: renewalDate.toISOString().slice(0, 10),
    reminderDate: reminderDate.toISOString().slice(0, 10),
    daysUntilRenewal: Math.ceil(
      (renewalDate - new Date()) / (1000 * 60 * 60 * 24)
    ),
  }
}];

This logic would require an unreasonable number of IF and DateTime nodes to replicate on the canvas.

Related: Use Edit Fields in "Map Each" Mode for Simple Renames · Configure Payload Size and Binary Data Mode for Large Files

Showcase builds

19 complete workflows from my own projects, each with its n8n workflow JSON to import. Showcase entries link the file at the end of the article.

See the showcase builds

Keep reading

190 entries grouped by topic, from first workflow to queue mode. Free, no signup.

Browse the encyclopedia

Need it built?

I design, build and run n8n systems for clients. Every engagement starts with a $1,500 diagnostic audit, credited toward the build.

Book an introductory call