Free Date Calculator Widget
Count days between dates or add and subtract days from any date.
A clean date calculator your visitors can use to count the days between two dates, or add and subtract any number of days from a date to find the resulting day. It recalculates live as they type, so it is perfect for booking sites, event pages, and project-planning tools. Install it with a single script tag — no dependencies, no accounts.
Embed this widget
Paste either snippet into your page — a Custom HTML block on WordPress, Shopify, or Squarespace works too. Both snippets update live as you customize, so what you copy is exactly what you configured.
<script src="https://hayneslab.dev/widgets/date-calculator.js" async></script>
<!-- Free Date Calculator widget by haynes lab — https://hayneslab.dev/tools/widgets/date-calculator --><!-- Free Date Calculator widget by haynes lab — https://hayneslab.dev/tools/widgets/date-calculator -->
<script>
/*!
* haynes lab — Date Calculator widget (free)
* Docs & embed code: https://hayneslab.dev/tools/widgets/date-calculator
*/
(function () {
'use strict';
var PREFIX = 'hlw-date-calculator';
var STYLE_ID = 'hlw-date-calculator-styles';
var MS_PER_DAY = 86400000;
function findScript() {
return document.currentScript ||
document.querySelector('script[src*="widgets/date-calculator.js"]');
}
function injectStyles() {
if (document.getElementById(STYLE_ID)) return;
var css =
'.' + PREFIX + ', .' + PREFIX + ' * { box-sizing: border-box; }' +
'.' + PREFIX + ' { position: relative; max-width: 560px; margin: 0 auto; padding: 24px 24px 40px;' +
' font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;' +
' font-size: 16px; line-height: 1.5; color: #1f2937; background: #ffffff;' +
' border: 1px solid #e5e7eb; border-radius: 12px; }' +
'.' + PREFIX + '__title { margin: 0 0 16px; font-size: 18px; font-weight: 600; color: #111827; }' +
'.' + PREFIX + '__tabs { display: flex; gap: 6px; margin-bottom: 20px; border-bottom: 1px solid #e5e7eb; }' +
'.' + PREFIX + '__tab { flex: 1; padding: 10px 8px; border: 0; border-bottom: 2px solid transparent;' +
' background: none; font: inherit; font-size: 14px; font-weight: 600; color: #6b7280;' +
' cursor: pointer; margin-bottom: -1px; }' +
'.' + PREFIX + '__tab[aria-selected="true"] { color: var(--hlw-accent); border-bottom-color: var(--hlw-accent); }' +
'.' + PREFIX + '__tab:focus-visible { outline: 2px solid var(--hlw-accent); outline-offset: -2px; border-radius: 4px; }' +
'.' + PREFIX + '__panel { display: none; }' +
'.' + PREFIX + '__panel--active { display: block; }' +
'.' + PREFIX + '__field { margin-bottom: 14px; }' +
'.' + PREFIX + '__label { display: block; font-size: 13px; font-weight: 600; color: #374151; margin-bottom: 4px; }' +
'.' + PREFIX + '__input, .' + PREFIX + '__select { width: 100%; padding: 9px 10px; font: inherit; font-size: 15px;' +
' color: #1f2937; background: #ffffff; border: 1px solid #d1d5db; border-radius: 8px; }' +
'.' + PREFIX + '__input:focus-visible, .' + PREFIX + '__select:focus-visible {' +
' outline: 2px solid var(--hlw-accent); outline-offset: 1px; border-color: var(--hlw-accent); }' +
'.' + PREFIX + '__row { display: flex; gap: 12px; }' +
'.' + PREFIX + '__row .' + PREFIX + '__field { flex: 1; min-width: 0; }' +
'.' + PREFIX + '__result { margin-top: 18px; padding: 14px 16px; background: #f9fafb;' +
' border: 1px solid #e5e7eb; border-radius: 8px; text-align: center; }' +
'.' + PREFIX + '__result-value { display: block; font-size: 26px; font-weight: 700; color: var(--hlw-accent); }' +
'.' + PREFIX + '__result-note { display: block; font-size: 13px; color: #6b7280; margin-top: 2px; }' +
'.' + PREFIX + '__credit { position: absolute; right: 10px; bottom: 6px; font-size: 11px;' +
' display: inline-flex; align-items: center; gap: 4px; color: #9ca3af; text-decoration: none; }' +
'.' + PREFIX + '__credit svg { display: block; }' +
'.' + PREFIX + '__credit:hover { color: #6b7280; text-decoration: underline; }' +
'@media (max-width: 480px) { .' + PREFIX + ' { padding: 18px 16px 36px; }' +
' .' + PREFIX + '__row { flex-direction: column; gap: 0; } }';
var style = document.createElement('style');
style.id = STYLE_ID;
style.textContent = css;
document.head.appendChild(style);
}
function el(tag, className, text) {
var node = document.createElement(tag);
if (className) node.className = className;
if (text != null) node.textContent = text;
return node;
}
function logoSvg() {
var svgNS = 'http://www.w3.org/2000/svg';
var svg = document.createElementNS(svgNS, 'svg');
svg.setAttribute('viewBox', '0 0 32 32');
svg.setAttribute('width', '13');
svg.setAttribute('height', '13');
svg.setAttribute('aria-hidden', 'true');
svg.setAttribute('focusable', 'false');
function circle(cx, cy, r, attrs) {
var c = document.createElementNS(svgNS, 'circle');
c.setAttribute('cx', cx);
c.setAttribute('cy', cy);
c.setAttribute('r', r);
for (var key in attrs) c.setAttribute(key, attrs[key]);
svg.appendChild(c);
}
circle(16, 16, 16, { fill: '#0f0a0a' });
circle(16, 16, 12, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1, opacity: 0.3 });
circle(16, 16, 9, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1, opacity: 0.5 });
circle(16, 16, 6, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1.2, opacity: 0.9 });
circle(16, 16, 3, { fill: '#06b6d4' });
circle(24.5, 7.5, 2, { fill: '#f97316' });
circle(7, 16, 1.8, { fill: '#f97316' });
circle(20, 20, 1.5, { fill: '#f97316' });
return svg;
}
function pad(n) { return (n < 10 ? '0' : '') + n; }
function todayStr() {
var d = new Date();
return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate());
}
function formatDate(d) {
return d.toLocaleDateString(undefined, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
}
function buildField(labelText, input) {
var field = el('div', PREFIX + '__field');
var label = el('label', PREFIX + '__label', labelText);
var id = PREFIX + '-' + labelText.toLowerCase().replace(/[^a-z0-9]+/g, '-') + '-' + Math.random().toString(36).slice(2, 8);
input.id = id;
label.setAttribute('for', id);
field.appendChild(label);
field.appendChild(input);
return field;
}
function makeDateInput(value) {
var input = el('input', PREFIX + '__input');
input.type = 'date';
if (value) input.value = value;
return input;
}
function build(script) {
var accent = script.getAttribute('data-hl-accent') || '#f97316';
injectStyles();
var root = el('div', PREFIX);
root.style.setProperty('--hlw-accent', accent);
root.appendChild(el('h3', PREFIX + '__title', 'Date Calculator'));
// ---- Tabs ----
var tabs = el('div', PREFIX + '__tabs');
tabs.setAttribute('role', 'tablist');
var tabLabels = ['Days between dates', 'Add or subtract days'];
var tabButtons = tabLabels.map(function (label, i) {
var tab = el('button', PREFIX + '__tab', label);
tab.type = 'button';
tab.setAttribute('role', 'tab');
tab.setAttribute('aria-selected', i === 0 ? 'true' : 'false');
tabs.appendChild(tab);
return tab;
});
root.appendChild(tabs);
// ---- Panel 1: days between two dates ----
var panel1 = el('div', PREFIX + '__panel ' + PREFIX + '__panel--active');
panel1.setAttribute('role', 'tabpanel');
var startInput = makeDateInput(todayStr());
var endDefault = new Date();
endDefault.setDate(endDefault.getDate() + 30);
var endInput = makeDateInput(endDefault.getFullYear() + '-' + pad(endDefault.getMonth() + 1) + '-' + pad(endDefault.getDate()));
var row1 = el('div', PREFIX + '__row');
row1.appendChild(buildField('Start date', startInput));
row1.appendChild(buildField('End date', endInput));
panel1.appendChild(row1);
var result1 = el('div', PREFIX + '__result');
var value1 = el('span', PREFIX + '__result-value');
var note1 = el('span', PREFIX + '__result-note');
result1.appendChild(value1);
result1.appendChild(note1);
panel1.appendChild(result1);
root.appendChild(panel1);
function calcBetween() {
var a = startInput.value, b = endInput.value;
if (!a || !b) { value1.textContent = '—'; note1.textContent = 'Pick both dates to see the difference.'; return; }
var start = new Date(a + 'T00:00:00'), end = new Date(b + 'T00:00:00');
var days = Math.round((end - start) / MS_PER_DAY);
var abs = Math.abs(days);
value1.textContent = abs + (abs === 1 ? ' day' : ' days');
var weeks = Math.floor(abs / 7);
var rem = abs % 7;
var breakdown = weeks > 0 ? weeks + (weeks === 1 ? ' week' : ' weeks') + (rem ? ', ' + rem + (rem === 1 ? ' day' : ' days') : '') : '';
if (days < 0) {
note1.textContent = 'The end date is ' + abs + (abs === 1 ? ' day' : ' days') + ' before the start date.' + (breakdown ? ' (' + breakdown + ')' : '');
} else {
note1.textContent = breakdown ? 'That is ' + breakdown + '.' : 'Between the two selected dates.';
}
}
startInput.addEventListener('input', calcBetween);
endInput.addEventListener('input', calcBetween);
// ---- Panel 2: add/subtract N days ----
var panel2 = el('div', PREFIX + '__panel');
panel2.setAttribute('role', 'tabpanel');
var baseInput = makeDateInput(todayStr());
panel2.appendChild(buildField('Start date', baseInput));
var row2 = el('div', PREFIX + '__row');
var daysInput = el('input', PREFIX + '__input');
daysInput.type = 'number';
daysInput.min = '0';
daysInput.step = '1';
daysInput.value = '30';
row2.appendChild(buildField('Number of days', daysInput));
var dirSelect = el('select', PREFIX + '__select');
[['add', 'Add days'], ['subtract', 'Subtract days']].forEach(function (pair) {
var opt = el('option', null, pair[1]);
opt.value = pair[0];
dirSelect.appendChild(opt);
});
row2.appendChild(buildField('Direction', dirSelect));
panel2.appendChild(row2);
var result2 = el('div', PREFIX + '__result');
var value2 = el('span', PREFIX + '__result-value');
var note2 = el('span', PREFIX + '__result-note');
result2.appendChild(value2);
result2.appendChild(note2);
panel2.appendChild(result2);
root.appendChild(panel2);
function calcAdd() {
var base = baseInput.value;
var n = parseInt(daysInput.value, 10);
if (!base || isNaN(n) || n < 0) { value2.textContent = '—'; note2.textContent = 'Enter a start date and a number of days.'; return; }
var d = new Date(base + 'T00:00:00');
d.setDate(d.getDate() + (dirSelect.value === 'subtract' ? -n : n));
value2.textContent = formatDate(d);
note2.textContent = n + (n === 1 ? ' day ' : ' days ') + (dirSelect.value === 'subtract' ? 'before' : 'after') + ' ' + formatDate(new Date(base + 'T00:00:00')) + '.';
}
baseInput.addEventListener('input', calcAdd);
daysInput.addEventListener('input', calcAdd);
dirSelect.addEventListener('change', calcAdd);
// ---- Tab switching ----
tabButtons.forEach(function (tab, i) {
tab.addEventListener('click', function () {
tabButtons.forEach(function (t, ti) { t.setAttribute('aria-selected', ti === i ? 'true' : 'false'); });
var active1 = i === 0;
panel1.className = PREFIX + '__panel' + (active1 ? ' ' + PREFIX + '__panel--active' : '');
panel2.className = PREFIX + '__panel' + (active1 ? '' : ' ' + PREFIX + '__panel--active');
});
});
calcBetween();
calcAdd();
var credit = el('a', PREFIX + '__credit');
credit.appendChild(logoSvg());
credit.appendChild(document.createTextNode('Powered by haynes lab'));
credit.href = 'https://hayneslab.dev/tools/widgets/date-calculator';
credit.target = '_blank';
credit.rel = 'noopener';
root.appendChild(credit);
if (script.parentNode && script.parentNode.nodeName !== 'HEAD') {
script.insertAdjacentElement('afterend', root);
} else {
document.body.appendChild(root);
}
}
function init() {
var script = findScript();
if (!script) {
console.warn('[haynes lab date-calculator] Could not locate its own script tag. Nothing rendered.');
return;
}
build(script);
}
if (document.body) {
init();
} else {
document.addEventListener('DOMContentLoaded', init);
}
})();
</script>Configuration
Every option is an attribute on the script tag — the same attributes the customizer above exposes. All attributes are optional unless marked required — defaults apply when omitted.
| Attribute | Default | Description |
|---|---|---|
data-hl-accent | "#f97316" | Highlight color for the result values, active tab, and focus states. |
Frequently asked questions
How do I install the Date Calculator?
Paste the script snippet where you want the calculator to appear — it renders inline right after the tag with both modes ready: days between two dates, and add or subtract days from a date.
Can I change the colors to match my site?
Yes — set data-hl-accent to any hex color to restyle the result values, active tab underline, and input focus states.
Does it work on WordPress, Shopify, or Squarespace?
Yes — it is a single vanilla JS file with no dependencies, so it works on WordPress, Shopify, Squarespace, or any site that can embed a script tag.
More free widgets
Tip Calculator
Let visitors calculate tips and split the bill in seconds.
View toolCalculators & UtilitiesPercentage Calculator
Three everyday percentage calculators in one clean embeddable widget.
View toolCalculators & UtilitiesUnit Converter
Let visitors convert length, weight, temperature, and volume on your site.
View toolNeed a custom widget?
We build custom widgets, integrations, and whole products. Tell us what your site needs and we'll scope it.
Get in Touch