Every developer has shipped a date bug. Usually it works on your machine, passes tests, and then fails for a user in another country at 11:30 PM on the last day of the month.
Here are seven of the most common date and time bugs, with short examples and fast ways to debug them.
1. Treating local time as universal time
A timestamp without a time zone is ambiguous. "2026-03-08 09:00" means nothing until you know where it was recorded.
Rule of thumb: store and transmit in UTC, convert to local time only when displaying.
When you're debugging a log line and need to compare it across regions, a world clock or time zone converter is quicker than doing the offset math in your head.
2. Parsing date strings inconsistently
In JavaScript, these two look almost identical but behave differently:
new Date("2026-03-08"); // parsed as UTC midnight
new Date("2026-03-08T00:00:00"); // parsed as LOCAL midnight
A date-only ISO string is treated as UTC, while a date-time string without an offset is treated as local time. In a timezone behind UTC, the first one can display as the previous day.
Fix: always include an explicit offset or Z:
new Date("2026-03-08T00:00:00Z");
new Date("2026-03-08T00:00:00+06:00");
If you're unsure what a string means, run it through a date and time conversion tool to see the UTC and local versions side by side.
3. Mixing seconds and milliseconds
Unix timestamps are usually in seconds. JavaScript's Date.now() returns milliseconds.
const seconds = Math.floor(Date.now() / 1000); // Unix time in seconds
const ms = Date.now(); // milliseconds
new Date(seconds); // wrong: lands in January 1970
new Date(seconds * 1000); // correct
If a date shows up as 1970, you probably passed seconds where milliseconds were expected. A quick paste into a timestamp converter shows what a value really represents, and a 10-digit versus 13-digit value tells you the unit at a glance.
4. Assuming a day is always 24 hours
Daylight saving time breaks this. In regions that observe DST, a calendar day can be 23 or 25 hours long.
from datetime import datetime, timedelta, timezone
from zoneinfo import ZoneInfo
ny = ZoneInfo("America/New_York")
start = datetime(2026, 3, 7, 12, 0, tzinfo=ny)
end = start + timedelta(days=1) # same wall-clock time next day
print(end) # 2026-03-08 12:00:00-04:00
print((end.astimezone(timezone.utc) - start.astimezone(timezone.utc)))
# 23:00:00
In 2026, US clocks spring forward on March 8, so "one day later" is only 23 hours of elapsed time here. Decide whether you mean "same time tomorrow" or "24 hours later," because they're different operations.
Also remember that DST rules differ by country, and some regions don't observe it at all. Before scheduling anything across regions, check the current offsets in a time zone tool.
5. Month overflow
Adding months is not the same as adding 30 days.
const d = new Date(2026, 0, 31); // Jan 31, 2026
d.setMonth(d.getMonth() + 1);
console.log(d.toDateString()); // Tue Mar 03 2026
February 2026 has 28 days, so "Feb 31" overflows into March 3. If you need "end of next month" behavior, clamp the day explicitly or use a date library that handles it.
To sanity-check your results, a date calculator lets you add or subtract days, weeks and months and compare the output with your code. For leap years, week numbers and days in a month, the calendar and date reference tools are handy.
6. Off-by-one date differences
Are you counting the start date, the end date, or both? "How many days between Monday and Friday?" can reasonably be 4 or 5.
A reliable way to diff calendar dates in JavaScript is to normalize to UTC midnight first:
const MS_PER_DAY = 86_400_000;
function daysBetween(a, b) {
const utcA = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
const utcB = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
return Math.round((utcB - utcA) / MS_PER_DAY);
}
This avoids DST shifting the result by an hour and breaking a naive division. For business logic like SLAs, invoices and delivery windows, you often need working days instead, and business day tools and public holiday tools help you verify expected results.
7. Forgetting that the date changes across time zones
The same instant can be two different calendar dates in two places. This breaks things like daily reports, "created today" filters and birthday reminders.
const instant = new Date("2026-09-30T19:47:00Z");
const fmt = (tz) =>
new Intl.DateTimeFormat("en-US", {
timeZone: tz,
dateStyle: "medium",
timeStyle: "short",
}).format(instant);
console.log(fmt("America/New_York")); // Sep 30, 2026, 3:47 PM
console.log(fmt("Asia/Dhaka")); // Oct 1, 2026, 1:47 AM
Same moment, different day. If your feature depends on "today," decide whose today it is: the server's, the user's or the business's.
A quick debugging checklist
- Is the value in UTC, local time or unknown?
- Does the string include an offset?
- Seconds or milliseconds?
- Are you doing elapsed-time math or calendar math?
- Does DST apply in this date range?
- Whose "today" are we using?
Free tools for quick checks
When something looks off, I like to verify with an independent tool before touching the code. The Noloii Time, Date & Calendar Tools collection is free and runs in the browser. The ones I reach for most as a dev:
- Timestamp tools for Unix, epoch and ISO conversion
- Date & time conversion for UTC versus local
- Time zone tools and world clock for cross-region checks
- Date calculator for date differences and additions
- Calendar & date reference for week numbers, leap years and quarters
What's the nastiest date bug you've ever tracked down? Share it in the comments.
Top comments (0)