{
  "schemaVersion": 1,
  "slug": "business-days-calculator",
  "category": "date-time",
  "kind": "implementation",
  "independentValidation": false,
  "implementationHash": "5c90e1ed673340a78bd0fe9e5f7c7c238760af05d77a05d05c8fac7d6198902f",
  "provenance": "Generated from the calculation contract used by the published page. Publication is not external validation.",
  "sourceFiles": [
    "data/calculators/business-days-calculator.json",
    "src/engine.js"
  ],
  "runtimeSource": "/sources/engine.txt",
  "runtimeHash": "5c888d8898afbdee36267bb9977829e1e2ac568f6677c8ee87469c139864645c",
  "scope": "The compute function records implemented equations, constants, lookup tables and custom rules. Inputs record defaults and allowed values; outputs record precision and units. Defaults are examples, not recommendations. General background references are not evidence for this specific algorithm.",
  "publishedFormula": {
    "formula": "N = Σ_{d ∈ [start,end), 1 ≤ weekday(d) ≤ 5} 1",
    "vars": [
      [
        "N",
        "Business days between dates"
      ],
      [
        "start",
        "Start date (counted)"
      ],
      [
        "end",
        "End date (not counted)"
      ],
      [
        "weekday(d)",
        "0 = Sunday … 6 = Saturday"
      ]
    ],
    "note": "Counts Monday through Friday and excludes weekends only. Public holidays and local working-day calendars are not included."
  },
  "externalEvidence": null,
  "contract": {
    "compute": "const DAY=86400000,wd=['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];\nconst empty={businessDays:null,calendarDays:null,weekendDays:null,resultDate:null,resultWeekday:null};\nconst parse=s=>{const m=/^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(String(s));if(!m)return null;const y=+m[1],mo=+m[2],day=+m[3],x=new Date(0);x.setUTCFullYear(y,mo-1,day);x.setUTCHours(0,0,0,0);return x.getUTCFullYear()===y&&x.getUTCMonth()===mo-1&&x.getUTCDate()===day?x:null};\nconst a=parse(startDate);if(!a||!['between','add'].includes(mode))return{...empty,__error:'inputError'};\nif(mode==='add'){if(!Number.isInteger(businessDaysToAdd)||Math.abs(businessDaysToAdd)>500)return{...empty,__error:'inputError'};let r=new Date(a.getTime()),remaining=Math.abs(businessDaysToAdd);const dir=businessDaysToAdd<0?-1:1;while(remaining>0){r=new Date(r.getTime()+dir*DAY);const dw=r.getUTCDay();if(dw!==0&&dw!==6)remaining--;}return{...empty,resultDate:r.toISOString().slice(0,10),resultWeekday:wd[r.getUTCDay()]};}\nconst b=parse(endDate);if(!b)return{...empty,__error:'inputError'};const lo=a<b?a:b,hi=a<b?b:a,calendar=(hi-lo)/DAY;let count=Math.floor(calendar/7)*5;for(let i=0;i<calendar%7;i++){const day=new Date(lo.getTime()+i*DAY).getUTCDay();if(day!==0&&day!==6)count++;}return{...empty,businessDays:count,calendarDays:calendar,weekendDays:calendar-count};",
    "inputs": [
      {
        "name": "mode",
        "type": "select",
        "default": "between",
        "options": [
          {
            "value": "between"
          },
          {
            "value": "add"
          }
        ]
      },
      {
        "name": "startDate",
        "type": "date",
        "default": "@today",
        "step": 1
      },
      {
        "name": "endDate",
        "type": "date",
        "default": "@today+1m",
        "step": 1,
        "showWhen": {
          "mode": [
            "between"
          ]
        }
      },
      {
        "name": "businessDaysToAdd",
        "type": "number",
        "default": 10,
        "min": -500,
        "max": 500,
        "step": 1,
        "slider": true,
        "showWhen": {
          "mode": [
            "add"
          ]
        }
      }
    ],
    "outputs": [
      {
        "name": "businessDays",
        "precision": 0,
        "unit": "days",
        "showWhen": {
          "mode": [
            "between"
          ]
        },
        "optional": true
      },
      {
        "name": "calendarDays",
        "precision": 0,
        "unit": "days",
        "showWhen": {
          "mode": [
            "between"
          ]
        },
        "optional": true
      },
      {
        "name": "weekendDays",
        "precision": 0,
        "unit": "days",
        "showWhen": {
          "mode": [
            "between"
          ]
        },
        "optional": true
      },
      {
        "name": "resultDate",
        "precision": 0,
        "unit": "",
        "type": "text",
        "showWhen": {
          "mode": [
            "add"
          ]
        },
        "optional": true
      },
      {
        "name": "resultWeekday",
        "precision": 0,
        "unit": "",
        "type": "text",
        "showWhen": {
          "mode": [
            "add"
          ]
        },
        "optional": true
      }
    ],
    "interpret": null
  }
}
