{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"date-calculator","type":"registry:component","title":"Date Difference Calculator","description":"Compare two dates and count the days between them.","author":"Notion Arena","categories":["time"],"tags":["widget","free","date calculator","days","deadline","calendar","planning"],"meta":{"kind":"widget","tier":"free","version":"1.0.0","updatedAt":"2026-10-11","notionPlan":"Free","emoji":"🗓️","delivery":["embed","html-block","download"],"capabilities":{"interactive":true,"offlineSafe":true,"persistsLocally":false,"writesToNotion":false,"needsNetwork":false},"embedUrl":"https://notionarena.vercel.app/embed/date-calculator","fileUrl":"https://notionarena.vercel.app/widgets/date-calculator.html","downloadUrl":"https://notionarena.vercel.app/api/widget/date-calculator","height":270,"minHeight":205,"maxHeight":380,"params":[{"key":"label","label":"Widget title","type":"text","default":"Date calculator"},{"key":"from","label":"Start date","type":"date","default":""},{"key":"to","label":"End date","type":"date","default":""},{"key":"inclusive","label":"Count both dates","type":"boolean","default":false},{"key":"accent","label":"Accent","type":"color","default":"#2383e2"},{"key":"theme","label":"Theme","type":"select","default":"auto","options":[{"value":"auto","label":"Match Notion"},{"value":"light","label":"Light"},{"value":"dark","label":"Dark"}]}]},"files":[{"path":"widgets/date-calculator.html","type":"registry:file","target":"notion/widgets/date-calculator.html","content":"<!doctype html>\n<html lang=\"en\" data-theme=\"auto\">\n<head>\n<meta charset=\"utf-8\" /><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n<title>Date Calculator</title>\n<style>\n:root{--bg:#fff;--fg:#37352f;--muted:#787774;--surface:#f7f7f5;--border:#e9e9e7;--accent:#2383e2;--font:ui-sans-serif,-apple-system,BlinkMacSystemFont,\"Segoe UI\",Helvetica,Arial,sans-serif}\nhtml[data-theme=\"dark\"]{--bg:#191919;--fg:#e6e6e4;--muted:#9b9a97;--surface:#252525;--border:#343434}\n@media(prefers-color-scheme:dark){html[data-theme=\"auto\"]{--bg:#191919;--fg:#e6e6e4;--muted:#9b9a97;--surface:#252525;--border:#343434}}\n*{box-sizing:border-box}html,body{margin:0;height:100%}body{background:var(--bg);color:var(--fg);font-family:ui-sans-serif,-apple-system,BlinkMacSystemFont,\"Segoe UI\",Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;overflow:hidden}\n.wrap{height:100%;min-height:205px;display:flex;flex-direction:column;gap:11px;padding:14px;overflow:auto}.head{display:flex;align-items:center;justify-content:space-between;gap:8px}.kicker{font-size:9px;text-transform:uppercase;letter-spacing:.1em;color:var(--muted);font-weight:650}.title{font-size:14px;font-weight:650;margin-top:2px}\n.inputs{display:grid;grid-template-columns:1fr auto 1fr;align-items:end;gap:8px}.field{display:flex;flex-direction:column;gap:4px;min-width:0}.field label{font-size:10px;color:var(--muted);font-weight:600}.field input{width:100%;min-width:0;height:34px;border:1px solid var(--border);border-radius:8px;background:var(--surface);color:var(--fg);padding:5px 7px;font:inherit;font-size:11px}.field input:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.arrow{padding-bottom:9px;color:var(--muted);font-size:16px}\n.result{display:flex;flex:1;min-height:60px;flex-direction:column;align-items:center;justify-content:center;gap:4px;border:1px solid var(--border);border-radius:10px;background:var(--surface);text-align:center;padding:8px}.number{font-size:30px;font-weight:650;letter-spacing:-.05em;font-variant-numeric:tabular-nums;color:var(--accent)}.unit{font-size:11px;font-weight:600}.detail{font-size:10px;color:var(--muted)}.foot{font-size:9.5px;color:var(--muted);text-align:center}\n@media(max-width:350px){.wrap{padding:10px}.inputs{gap:5px}.field input{font-size:10px}.number{font-size:26px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}\n</style>\n</head>\n<body><main class=\"wrap\">\n  <header class=\"head\"><div><div class=\"kicker\">Date math</div><div class=\"title\" id=\"title\">Date calculator</div></div></header>\n  <section class=\"inputs\" aria-label=\"Choose two dates\"><div class=\"field\"><label for=\"from\">From</label><input id=\"from\" type=\"date\" /></div><div class=\"arrow\" aria-hidden=\"true\">→</div><div class=\"field\"><label for=\"to\">To</label><input id=\"to\" type=\"date\" /></div></section>\n  <section class=\"result\" aria-live=\"polite\"><div class=\"number\" id=\"days\">30</div><div class=\"unit\" id=\"unit\">days apart</div><div class=\"detail\" id=\"weekdays\">22 weekdays in range</div></section>\n  <div class=\"foot\" id=\"note\">Weekdays exclude weekends, not local holidays.</div>\n</main>\n<script>\n(function(){\n  var cfgEl=document.getElementById('bk-config'),base={};try{base=cfgEl?JSON.parse(cfgEl.textContent||'{}'):{};}catch(e){}\n  var q=new URLSearchParams(location.search),c={};for(var k in base)c[k]=base[k];q.forEach(function(v,k){c[k]=v;});\n  function bool(k,d){var v=c[k];return v===undefined?d:(v===true||v==='true'||v==='1');}\n  function safeColor(v,d){return /^#[0-9a-f]{6}$/i.test(String(v||''))?String(v):d;}\n  function valid(value){return /^\\d{4}-\\d{2}-\\d{2}$/.test(value)&&!isNaN(new Date(value+'T00:00:00Z').getTime());}\n  function iso(d){return d.getFullYear()+'-'+String(d.getMonth()+1).padStart(2,'0')+'-'+String(d.getDate()).padStart(2,'0');}\n  var theme=['auto','light','dark'].indexOf(String(c.theme||'auto'))>-1?String(c.theme||'auto'):'auto',ACCENT=safeColor(c.accent,'#2383e2'),LABEL=String(c.label||'Date calculator').slice(0,48),INCLUSIVE=bool('inclusive',false);\n  document.documentElement.setAttribute('data-theme',theme);document.documentElement.style.setProperty('--accent',ACCENT);document.getElementById('title').textContent=LABEL;document.title=LABEL;\n  var from=document.getElementById('from'),to=document.getElementById('to');\n  var today=new Date(),later=new Date(today.getFullYear(),today.getMonth(),today.getDate()+30);\n  from.value=valid(String(c.from||''))?String(c.from):iso(today);to.value=valid(String(c.to||''))?String(c.to):iso(later);\n  function stamp(value){if(!valid(value))return NaN;var p=value.split('-').map(Number);return Date.UTC(p[0],p[1]-1,p[2]);}\n  function paint(){\n    var a=stamp(from.value),b=stamp(to.value),daysOut=document.getElementById('days'),unit=document.getElementById('unit'),weekdays=document.getElementById('weekdays');\n    if(!isFinite(a)||!isFinite(b)){daysOut.textContent='...';unit.textContent='choose two dates';weekdays.textContent='';return;}\n    var lo=Math.min(a,b),hi=Math.max(a,b),apart=Math.round((hi-lo)/864e5),count=apart+(INCLUSIVE?1:0),weekdayCount=0;\n    for(var cursor=lo+(INCLUSIVE?0:864e5);cursor<(INCLUSIVE?hi+864e5:hi);cursor+=864e5){var day=new Date(cursor).getUTCDay();if(day!==0&&day!==6)weekdayCount++;}\n    daysOut.textContent=String(count);unit.textContent=INCLUSIVE?'calendar days, inclusive':'days apart';weekdays.textContent=weekdayCount+' weekdays in range';\n  }\n  from.addEventListener('input',paint);to.addEventListener('input',paint);paint();\n})();\n</script>\n</body>\n</html>\n"}]}