What you'll be able to do
By the end of this page you should be able to create a Date, show a readable string in the UI, compare two moments, and recognize a few classic timezone/parsing traps — without memorizing every calendar API.
Previous post: modules — named, default, and dynamic import.
Who this is for
- People who need "posted 3 days ago" or a simple due date on a form
- Beginners who saw
new Date("2026-08-13")shift by a day and thought JS was broken - Anyone formatting dates with string concat and getting
NaNsurprises
You can skip this if you already use a date library on purpose and know why. Here we stay with built-in Date for foundations.
Create dates you understand
const now = new Date();
const fromParts = new Date(2026, 7, 13); // month is 0-based: 7 = August
Scenario A — "now" for a timestamp. new Date() is fine for "saved at."
Scenario B — a specific calendar day. Prefer new Date(y, monthIndex, day) when you control the parts, because month indexing is explicit once you remember it.
The rule: months are 0–11 in the Date constructor with numbers — August is 7, not 8.
Format for humans (keep it simple)
const d = new Date(2026, 7, 13, 15, 30);
console.log(d.toLocaleDateString());
console.log(d.toLocaleString());
Scenario A — show a date on a card. toLocaleDateString() respects the user's locale without you building DD/MM by hand.
Scenario B — need a fixed shape for logs. toISOString() is great for machines; it is UTC-based, so do not paste it raw into a "local birthday" label without thinking.
Compare and sort
const a = new Date(2026, 0, 1);
const b = new Date(2026, 0, 2);
console.log(a.getTime() < b.getTime()); // true
Scenario A — sort events. Compare .getTime() (milliseconds) so you are not fighting object identity.
Scenario B — "is overdue?" due.getTime() < Date.now().
Parsing strings — the sharp edge
// Risky: date-only ISO may be treated as UTC midnight
const risky = new Date("2026-08-13");
Scenario A — user typed in a form. Prefer reading year/month/day fields and building with new Date(y, m - 1, d).
Scenario B — API sent full ISO with time and offset. new Date(isoString) is usually fine when the string includes timezone info.
A tiny practice file
<!DOCTYPE html>
<html>
<body>
<label>Year <input id="y" type="number" value="2026" /></label>
<label>Month <input id="m" type="number" value="8" min="1" max="12" /></label>
<label>Day <input id="d" type="number" value="13" /></label>
<button id="go">Show</button>
<p id="out"></p>
<script>
document.getElementById("go").addEventListener("click", function () {
const y = Number(document.getElementById("y").value);
const m = Number(document.getElementById("m").value) - 1;
const day = Number(document.getElementById("d").value);
const date = new Date(y, m, day);
document.getElementById("out").textContent =
date.toLocaleDateString() + " | ms=" + date.getTime();
});
</script>
</body>
</html>
Mistakes I see a lot
1. Using month 8 for August in new Date(y, 8, 1) — that is September.
2. Subtracting Date objects as if they were day counts without dividing by ms-per-day carefully (DST can still surprise you).
3. Showing toISOString() to end users and wondering why the calendar day looks "wrong."
4. Mutating one Date while sorting — prefer copying or using timestamps.
What to try before the next post
- Build the tiny form and print local vs
toISOString(). - Sort three event dates with
.getTime(). - Compute days between two dates using ms difference / (24*60*60*1000) and note DST limits.
Next in this series: regexp basics for forms — light pattern checks for email-ish and digits, without becoming a regex specialist.
Try this next outside the series
Deeper JavaScript pays off when you connect it to routes, imports, and performance-sensitive app code.
- Next.js route handlers — see modules and request logic in a production-shaped route file
- HTML to JSX Converter — use the tool when deeper syntax work starts touching React files