Calculators & Utilities

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.

Live demo

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.

Hosted (recommended)html
<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 -->
Inline (self-contained)html
<!-- 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.

AttributeDefaultDescription
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

Need 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