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 NaN surprises

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

  1. Build the tiny form and print local vs toISOString().
  2. Sort three event dates with .getTime().
  3. 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.