Hi! May I seek for your help on how to calculate the number of days between two dates excluding weekends and public holidays using javascript. Also, how to calculate the end date (start date and duration given) excluding weekends and public holidays. Thank you.

Dani AI

Generated

As requested, below is a compact, reliable JavaScript approach that (1) counts business days between two dates (weekends + a supplied list of public holidays excluded) and (2) computes an end date given a start date and a duration measured in business days. This version normalizes dates to UTC-midnight (avoiding DST/timezone drift), supports inclusive/exclusive semantics, and expects holidays as explicit 'YYYY-MM-DD' entries. (As asked, date parsing is handled; thanks to for pointing to related solutions.)

// Simple, correct utilities using UTC-normalized day numbers.
// Inputs: Date objects or 'YYYY-MM-DD' strings. Holidays: ['YYYY-MM-DD', ...].

const MS_PER_DAY = 24 * 60 * 60 * 1000;

function toDayNumber(d) {
  if (d instanceof Date) {
    return Math.floor(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()) / MS_PER_DAY);
  }
  if (typeof d === 'string') {
    const parts = d.split('-').map(Number);
    if (parts.length !== 3) throw new Error('Date string must be YYYY-MM-DD');
    return Math.floor(Date.UTC(parts[0], parts[1] - 1, parts[2]) / MS_PER_DAY);
  }
  throw new Error('toDayNumber accepts Date or YYYY-MM-DD string');
}

function dayNumberToDate(n) {
  return new Date(n * MS_PER_DAY);
}

function isWeekendDayNumber(n) {
  const dow = new Date(n * MS_PER_DAY).getUTCDay(); // 0 = Sun, 6 = Sat
  return dow === 0 || dow === 6;
}

function businessDaysBetween(start, end, holidays = [], opts) {
  const inclusive = opts && typeof opts.inclusive === 'boolean' ? opts.inclusive : true;
  let a = toDayNumber(start), b = toDayNumber(end);
  if (a > b) { const t = a; a = b; b = t; }
  if (!inclusive) { a += 1; b -= 1; }
  if (a > b) return 0;

  const days = b - a + 1;
  const wholeWeeks = Math.floor(days / 7);
  let business = wholeWeeks * 5;

  const extra = days % 7;
  const startDow = new Date(a * MS_PER_DAY).getUTCDay();
  for (let i = 0; i < extra; i++) {
    const dow = (startDow + i) % 7;
    if (dow !== 0 && dow !== 6) business++;
  }

  const holidaySet = new Set(holidays.map(h => toDayNumber(h)));
  for (const h of holidaySet) {
    if (h >= a && h <= b) {
      const dow = new Date(h * MS_PER_DAY).getUTCDay();
      if (dow !== 0 && dow !== 6) business--;
    }
  }
  return Math.max(0, business);
}

function addBusinessDays(start, businessDays, holidays = [], opts) {
  if (!Number.isInteger(businessDays)) throw new Error('businessDays must be an integer');
  const includeStart = opts && typeof opts.includeStart === 'boolean' ? opts.includeStart : true;
  const sign = businessDays >= 0 ? 1 : -1;
  let remaining = Math.abs(businessDays);

  const holidaySet = new Set(holidays.map(h => toDayNumber(h)));
  let candidate = toDayNumber(start);
  candidate = includeStart ? candidate : candidate + sign;

  if (remaining === 0) {
    while (isWeekendDayNumber(candidate) || holidaySet.has(candidate)) candidate += sign;
    return dayNumberToDate(candidate);
  }

  while (true) {
    if (!isWeekendDayNumber(candidate) && !holidaySet.has(candidate)) {
      remaining--;
      if (remaining === 0) return dayNumberToDate(candidate);
    }
    candidate += sign;
  }
}

Notes and troubleshooting

  • Input conventions: use Date objects or 'YYYY-MM-DD' strings; holidays must be explicit dates (include observed dates if a holiday shifts from a weekend). The code treats holidays falling on weekends as already excluded (only weekday holidays reduce the count).
  • Semantics: businessDaysBetween defaults to inclusive endpoints (pass { inclusive: false } to exclude). addBusinessDays(..., n) treats start as day 1 by default; pass { includeStart: false } to start counting from the following business day.
  • Timezones: normalization to UTC-midnight avoids DST/local-offset problems. Always provide date-only values when possible.
  • Performance: counting uses week arithmetic (no per-day loop) except when advancing by N business days; the latter uses a simple loop which is fine for typical durations. For very large distances, accelerate by adding whole weeks and then correcting for holidays (requires counting holidays inside the fast-forwarded span).
  • Custom weekends: for locales with different weekend days, adjust isWeekendDayNumber accordingly.

Edge cases covered: start > end, zero-length intervals, negative durations, and duplicate holiday entries (deduplicated via Set).

Recommended Answers

All 2 Replies

What do you have so far?

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.