{
  "schemaVersion": 1,
  "slug": "passport-expiry-calculator",
  "category": "travel",
  "kind": "implementation",
  "independentValidation": false,
  "implementationHash": "840d1ea9a855e0d1273253ad9c7f05abfb5a7ff621fd4a4ea718292685ea239f",
  "provenance": "Generated from the calculation contract used by the published page. Publication is not external validation.",
  "sourceFiles": [
    "data/calculators/passport-expiry-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": "v = 1 when expiry ≥ D_rule; M ≈ (expiry − D_dep) ÷ 30.44",
    "vars": [
      [
        "expiry",
        "Passport expiry date"
      ],
      [
        "D_dep",
        "Trip start / entry date"
      ],
      [
        "D_ret",
        "Trip return / destination-exit date"
      ],
      [
        "D_rule",
        "Date threshold selected by the rule"
      ],
      [
        "M",
        "Approximate months remaining at departure (30.44 days)"
      ]
    ],
    "note": "Compares expiry with one selected date rule using UTC calendar dates; adding months clamps to the final valid day in the target month. It does not verify destination or nationality rules, passport issue date, visas, transit, or blank pages. Confirm with current official destination guidance."
  },
  "externalEvidence": {
    "reviewedAt": "2026-09-26",
    "sources": [
      {
        "name": "U.S. Department of State — Passport FAQ",
        "url": "https://travel.state.gov/en/passports/contact-support/faq.html"
      },
      {
        "name": "U.S. Department of State — Traveler’s Checklist",
        "url": "https://travel.state.gov/content/travel/en/international-travel/before-you-go/travelers-checklist.html"
      }
    ]
  },
  "contract": {
    "compute": "const day=86400000,utc=(y,m,dt)=>{const x=new Date(0);x.setUTCHours(0,0,0,0);x.setUTCFullYear(y,m,dt);return x;},parse=s=>{const m=/^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(String(s));if(!m)return null;const y=+m[1],mo=+m[2]-1,da=+m[3],x=utc(y,mo,da);return x.getUTCFullYear()===y&&x.getUTCMonth()===mo&&x.getUTCDate()===da?x:null;},addMonths=(d,n)=>{const y=d.getUTCFullYear(),m=d.getUTCMonth()+n,dt=d.getUTCDate(),first=utc(y,m,1),last=utc(first.getUTCFullYear(),first.getUTCMonth()+1,0).getUTCDate();return utc(first.getUTCFullYear(),first.getUTCMonth(),Math.min(dt,last));},expiry=parse(expiryDate),departure=parse(departureDate),ret=parse(returnDate);if(!expiry||!departure||!ret||ret<departure)return{__error:'inputError',monthsRemaining:null,requiredUntil:null,validityStatus:null,renewBy:null,note:null};const fmt=d=>d.toISOString().slice(0,10),reqMap={threeMonths:[addMonths(ret,3),'Three months beyond entered return/exit date'],sixMonths:[addMonths(departure,6),'Six months beyond departure'],sixMonthsReturn:[addMonths(ret,6),'Six months beyond return'],validOnEntry:[departure,'Valid on the date of entry']},entry=reqMap[requirement]||reqMap.sixMonths,requiredUntil=entry[0],ok=expiry>=requiredUntil,monthsRemaining=(expiry-departure)/(day*30.44);return{monthsRemaining,requiredUntil:fmt(requiredUntil),validityStatus:ok?'Valid — meets the selected date rule':'Does not meet the selected date rule',renewBy:'Check current passport-office processing and mailing times',note:'Entry rules vary by destination and may depend on nationality, transit, and trip dates. Verify with official destination guidance; some countries also require blank visa pages.'};",
    "inputs": [
      {
        "name": "expiryDate",
        "type": "date",
        "default": "@today+3m",
        "step": 1
      },
      {
        "name": "departureDate",
        "type": "date",
        "default": "@today+2m",
        "step": 1
      },
      {
        "name": "returnDate",
        "type": "date",
        "default": "@today+10w",
        "step": 1
      },
      {
        "name": "requirement",
        "type": "select",
        "default": "sixMonths",
        "options": [
          {
            "value": "threeMonths"
          },
          {
            "value": "sixMonths"
          },
          {
            "value": "sixMonthsReturn"
          },
          {
            "value": "validOnEntry"
          }
        ]
      }
    ],
    "outputs": [
      {
        "name": "monthsRemaining",
        "precision": 1,
        "unit": "months"
      },
      {
        "name": "requiredUntil",
        "precision": 0,
        "unit": "",
        "type": "text"
      },
      {
        "name": "validityStatus",
        "precision": 0,
        "unit": "",
        "type": "text"
      },
      {
        "name": "renewBy",
        "precision": 0,
        "unit": "",
        "type": "text"
      },
      {
        "name": "note",
        "precision": 0,
        "unit": "",
        "type": "text"
      }
    ],
    "interpret": null
  }
}
