← Business Days Calculator

Add Business Days to a Date in Python, JavaScript and SQL

To add N business days, step one day at a time from the day after the start date and count only days that are not Saturday, Sunday or a holiday. Stop when the count reaches N.

Just need one date? Use the free calculator and pick “Add or subtract business days”.

The rules every snippet follows

The holiday you will miss: New Year's Day 2028 falls on 31 Dec 2027

1 Jan 2028 is a Saturday, so the federal day off moves back to Friday 31 December 2027. Code that builds a holiday list one year at a time puts that date under 2028 and never checks it while you are working in 2027.

2027 has three more moved holidays: Juneteenth on Friday 18 June, Independence Day on Monday 5 July and Christmas on Friday 24 December. All of them are already in the lists below.

Python

Standard library only. Works with Python 3.7 and later.

from datetime import date, timedelta

# US federal holidays 2026-2027, observed dates (when offices are actually closed)
US_HOLIDAYS = {date.fromisoformat(d) for d in [
    "2026-01-01", "2026-01-19", "2026-02-16", "2026-05-25", "2026-06-19", "2026-07-03",
    "2026-09-07", "2026-10-12", "2026-11-11", "2026-11-26", "2026-12-25",
    "2027-01-01", "2027-01-18", "2027-02-15", "2027-05-31", "2027-06-18", "2027-07-05",
    "2027-09-06", "2027-10-11", "2027-11-11", "2027-11-25", "2027-12-24", "2027-12-31",
]}

def add_business_days(start: date, n: int, holidays=US_HOLIDAYS) -> date:
    """Move n business days from start, skipping Saturdays, Sundays and holidays.
    n > 0 counts forward from the next day, n < 0 counts backward, n == 0 returns start."""
    step = 1 if n > 0 else -1
    day = start
    remaining = abs(n)
    while remaining:
        day += timedelta(days=step)
        if day.weekday() < 5 and day not in holidays:
            remaining -= 1
    return day

print(add_business_days(date(2026, 11, 20), 10))  # 2026-12-07

JavaScript

Works in the browser and in Node.js. Dates are handled in UTC, so a daylight-saving change can never push the result to the wrong day.

// US federal holidays 2026-2027, observed dates (when offices are actually closed)
const US_HOLIDAYS = new Set([
  "2026-01-01", "2026-01-19", "2026-02-16", "2026-05-25", "2026-06-19", "2026-07-03",
  "2026-09-07", "2026-10-12", "2026-11-11", "2026-11-26", "2026-12-25",
  "2027-01-01", "2027-01-18", "2027-02-15", "2027-05-31", "2027-06-18", "2027-07-05",
  "2027-09-06", "2027-10-11", "2027-11-11", "2027-11-25", "2027-12-24", "2027-12-31",
]);

// Move n business days from an ISO date (YYYY-MM-DD), skipping weekends and holidays.
// n > 0 counts forward from the next day, n < 0 counts backward, n === 0 returns the start date.
function addBusinessDays(isoDate, n, holidays = US_HOLIDAYS) {
  const d = new Date(isoDate + "T00:00:00Z"); // UTC: daylight saving cannot shift the day
  const step = n > 0 ? 1 : -1;
  let remaining = Math.abs(n);
  while (remaining > 0) {
    d.setUTCDate(d.getUTCDate() + step);
    const weekday = d.getUTCDay();
    const iso = d.toISOString().slice(0, 10);
    if (weekday !== 0 && weekday !== 6 && !holidays.has(iso)) remaining--;
  }
  return d.toISOString().slice(0, 10);
}

console.log(addBusinessDays("2026-11-20", 10)); // 2026-12-07

SQL: PostgreSQL

A holiday table plus a small function you can call from any query, for example UPDATE orders SET due_date = add_business_days(order_date, 5).

-- US federal holidays 2026-2027, observed dates (when offices are actually closed)
CREATE TABLE us_holidays (day date PRIMARY KEY);
INSERT INTO us_holidays (day) VALUES
  ('2026-01-01'), ('2026-01-19'), ('2026-02-16'), ('2026-05-25'), ('2026-06-19'), ('2026-07-03'), ('2026-09-07'), ('2026-10-12'), ('2026-11-11'), ('2026-11-26'), ('2026-12-25'), ('2027-01-01'), ('2027-01-18'), ('2027-02-15'), ('2027-05-31'), ('2027-06-18'), ('2027-07-05'), ('2027-09-06'), ('2027-10-11'), ('2027-11-11'), ('2027-11-25'), ('2027-12-24'), ('2027-12-31');

-- Move n business days from start_date, skipping weekends and holidays.
-- n > 0 counts forward from the next day, n < 0 counts backward, n = 0 returns start_date.
CREATE OR REPLACE FUNCTION add_business_days(start_date date, n integer)
RETURNS date LANGUAGE plpgsql STABLE AS $$
DECLARE
  d date := start_date;
  step integer := CASE WHEN n > 0 THEN 1 ELSE -1 END;
  remaining integer := abs(n);
BEGIN
  WHILE remaining > 0 LOOP
    d := d + step;
    IF extract(isodow FROM d) < 6
       AND NOT EXISTS (SELECT 1 FROM us_holidays WHERE day = d) THEN
      remaining := remaining - 1;
    END IF;
  END LOOP;
  RETURN d;
END $$;

SELECT add_business_days(DATE '2026-11-20', 10);  -- 2026-12-07

SQL: SQLite

SQLite has no stored functions, so this is a single query. Put your start date and n in the params line (n appears twice).

-- US federal holidays 2026-2027, observed dates (when offices are actually closed)
CREATE TABLE us_holidays (day TEXT PRIMARY KEY);
INSERT INTO us_holidays (day) VALUES
  ('2026-01-01'), ('2026-01-19'), ('2026-02-16'), ('2026-05-25'), ('2026-06-19'), ('2026-07-03'), ('2026-09-07'), ('2026-10-12'), ('2026-11-11'), ('2026-11-26'), ('2026-12-25'), ('2027-01-01'), ('2027-01-18'), ('2027-02-15'), ('2027-05-31'), ('2027-06-18'), ('2027-07-05'), ('2027-09-06'), ('2027-10-11'), ('2027-11-11'), ('2027-11-25'), ('2027-12-24'), ('2027-12-31');

-- Change the start date and n in the first line. n < 0 counts backward, n = 0 returns the start date.
WITH RECURSIVE
  params(start, n, step) AS (
    SELECT '2026-11-20', 10, CASE WHEN 10 < 0 THEN '-1 day' ELSE '+1 day' END
  ),
  walk(d, counted) AS (
    SELECT start, 0 FROM params
    UNION ALL
    SELECT date(d, step),
           counted + (strftime('%w', date(d, step)) NOT IN ('0', '6')
                      AND date(d, step) NOT IN (SELECT day FROM us_holidays))
    FROM walk, params
    WHERE counted < abs(n)
  )
SELECT d FROM walk, params WHERE counted = abs(n) LIMIT 1;  -- 2026-12-07

Check your own code with these dates

Run your function on these inputs. Every snippet on this page returns exactly these results.

StartnResultWhy
Fri 2026-11-2010Mon 2026-12-07Thanksgiving skipped
Thu 2026-07-021Mon 2026-07-06July 4 is a Saturday, office closed Fri 3 July
Thu 2026-12-241Mon 2026-12-28Christmas skipped
Thu 2027-06-171Mon 2027-06-21Juneteenth observed Fri 18 June
Thu 2027-12-301Mon 2028-01-03New Year 2028 observed Fri 31 Dec 2027
Mon 2026-11-30-5Fri 2026-11-20counting backward, Thanksgiving skipped
Sat 2026-03-071Mon 2026-03-09start on a weekend
Sat 2026-03-07-1Fri 2026-03-06backward from a weekend
Sat 2026-03-070Sat 2026-03-070 returns the start date

How these snippets were tested

Each snippet was run on 400 start dates between March 2026 and October 2027 with n from −40 to 40, plus the cases in the table above. The results were compared with the calculator on this site: 400 out of 400 matched for Python 3.12, Node.js 22, SQLite 3.53 and PostgreSQL 18.

Common mistakes

Questions

Does adding business days include the start date?

No. Counting starts on the next day. 1 business day after Monday is Tuesday, the same as WORKDAY in Excel.

What happens if the start date is a Saturday?

Adding 1 business day gives the following Monday. Subtracting 1 gives the Friday before.

How do I subtract business days?

Pass a negative number. −5 from Monday 30 Nov 2026 returns Friday 20 Nov 2026.

Can I use a different weekend?

Yes. Change the weekend test: day.weekday() < 5 in Python, weekday !== 0 && weekday !== 6 in JavaScript, isodow < 6 in PostgreSQL, NOT IN ('0', '6') in SQLite.