I was working on a project timeline feature when I hit an unexpected wall. The requirement seemed simple: "Show how many days are between these two dates." Easy, right? (end - start) / 86400000. Done.
Except it wasn't done. Because "days between" means completely different things depending on whether you're planning a sprint, calculating payroll, or tracking a contract period. And that's before we even get into time zones.
The Problem with "Just Subtract the Dates"
Here's the thing that trips up most developers (myself included): a date difference isn't just one number. It's several different numbers, each answering a different question:
- Total days: The raw duration in 24-hour blocks
-
Calendar breakdown: How many full years, months, and days? (Spoiler: it's not just
totalDays / 365) - Working days: How many weekdays? (Because weekends don't exist when you're on a deadline)
I needed all three, and I needed them to be accurate. Existing libraries like date-fns or dayjs handle some of this, but they're heavy for what I needed. Plus, I'm a bit of a minimalist when it comes to dependencies — adding a 50KB library to calculate what should be a few simple functions felt like using a chainsaw to cut a piece of string.
So I decided to build it myself. Because apparently I enjoy reinventing wheels.
The Time Zone Trap
The first thing that bit me: time zones. JavaScript's Date object is notoriously tricky with this. If you parse "2026-08-01" with new Date("2026-08-01"), you get UTC midnight — but if you're in a different timezone, that's actually the previous evening for you.
The fix was to use datetime-local inputs and parse everything in local time. No UTC conversions, no getTimezoneOffset() gymnastics. Here's the core parsing function that saved my sanity:
function parseLocal(dt) {
if (!dt) return null;
const d = new Date(dt);
return isNaN(d.getTime()) ? null : d;
}
That's it. The datetime-local input format (YYYY-MM-DDTHH:mm) is designed to be parsed as local time, so new Date() handles it correctly. The key insight: don't try to be clever with timezones when you don't need to be.
The Calendar Breakdown Problem
Now for the interesting part. I needed to break down a date range into years, months, and days. This sounds straightforward until you realize that months have varying lengths, and "1 month from January 31" is genuinely ambiguous.
I went with a calendar-push approach: increment the start date by years until you can't go further without exceeding the end date, then months, then days. Here's the logic that made it work:
function calendarBreakdown(start, end) {
let y = 0, mo = 0, d = 0;
let temp = new Date(start);
while (addMonths(temp, y + 1) <= end) y++;
temp = addMonths(new Date(start), y);
while (addMonths(temp, mo + 1) <= end) mo++;
temp = addMonths(temp, mo);
d = Math.floor((end - temp) / 86400000);
return { y, mo, d };
}
The addMonths function handles the edge cases — like February 29 — by clamping to the end of the month. It's not perfect (what is, with dates?), but it gives results that match human intuition about calendar spans.
Working Days: The Weekend Problem
The workday calculation sounded easy: iterate through dates, skip Saturdays and Sundays. But here's a subtlety that caught me: should the workday count include the start and end dates? Most people think "between" means excluding endpoints, but in business contexts, you usually want inclusive counting.
I went with inclusive counting, and the implementation is refreshingly simple:
function countWorkdays(start, end) {
let count = 0;
const cur = new Date(start);
while (cur <= end) {
const day = cur.getDay();
if (day !== 0 && day !== 6) count++;
cur.setDate(cur.getDate() + 1);
}
return count;
}
The getDay() method returns 0 for Sunday and 6 for Saturday, so checking for those two values handles the entire weekend logic. No arrays, no constants, no lookup tables.
Where AI Actually Helped
Now, the part you're probably curious about: how much of this did I build with AI assistance?
I'll be honest — I used Claude for a significant portion of this. The interesting part was the iterative process. My first prompt was something like: "Write a date difference calculator that shows days, hours, and workdays."
What I got back was... technically correct but practically useless. It used UTC everywhere, which would have been fine if I was building for a server, but this was a browser tool. The AI didn't think about timezones because I didn't ask it to.
The back-and-forth went something like:
- Me: "The dates are off by one day when I test with local times."
- AI: "Ah, you need to use local time parsing. Here's the fix." (And it was right.)
- Me: "Now the calendar breakdown doesn't match what I expect for ranges crossing month boundaries."
- AI: "You need to use a calendar-push approach instead of just dividing total days."
The AI was genuinely helpful for catching edge cases I hadn't considered and for refactoring the logic. But it also made mistakes — the first version of the workday calculator excluded the start date, which I had to catch and fix.
My honest take: AI is excellent at generating correct code for well-defined problems, but it's terrible at understanding what "well-defined" means for your specific use case. You still need to be the domain expert.
The Tool That Emerged
During this process, I built a small browser-based tool to make this workflow easier. It's a date difference calculator that handles all three modes — total days, calendar breakdown, and workday counting — with a clean interface that doesn't require any server-side processing.
The UI is deliberately minimal: two date inputs, a checkbox for workday mode, and a results panel. The entire thing runs in the browser with zero dependencies, which means it's instant and works offline.
One design decision I'm particularly happy about: defaulting to showing "today → 30 days from now" as an example. It gives users immediate feedback and demonstrates the tool's capabilities without requiring them to figure out the interface first.
Lessons Learned
Time zones are the root of all evil. If you don't need to handle them, don't. Local time parsing with
datetime-localinputs sidesteps an entire class of bugs."Days between" is ambiguous. Always clarify whether you need total days, calendar days, or working days. They're all different numbers.
AI is a pair programmer, not a replacement. It caught edge cases I missed, but it also missed cases I caught. We complemented each other.
Dependencies aren't always worth it. For something this focused, a few well-written functions beat a library every time.
The tool is live at craftvo.app if you want to see the final result. It's free, it's fast, and it handles the date math so you don't have to.
Now if you'll excuse me, I need to go explain to my PM why "2 days" and "48 hours" are apparently different numbers.
Top comments (0)