cf.completefrontendCode editorOpen lab
THE JAVASCRIPT FIELD GUIDE

Date & time

Create, compare, parse, calculate, and format dates with JavaScript's Date object while avoiding time zone and locale traps.

By the end, you can
  • 01
    Think in timestampsTreat Date as a wrapper around one millisecond number.
  • 02
    Parse safelyPrefer ISO strings and know which forms are UTC or local.
  • 03
    Calculate and format deliberatelyUse UTC methods for deterministic math and locale APIs for people.

One number, many clocks

Dates feel human: birthdays, deadlines, calendars, time zones, holidays. JavaScript’s Date is much simpler and much stranger: it stores one number, a timestamp in milliseconds since 1970-01-01T00:00:00.000Z. Everything else is a way to read or format that number.

That mental model connects lessons you already know. A timestamp is a Number. JSON.stringify turns dates into ISO strings, and JSON.parse can revive them in the JSON lesson. +date uses Object to primitive conversion to get the timestamp. Comparisons work because dates can become numbers.

Real-life analogyA timestamp is one global stopwatch

Imagine one stopwatch for the whole world. It does not care whether you are in Kolkata, Mumbai, New York, or Tokyo. It just counts milliseconds. A Date object is a wrapper around that count. Time zones are the glasses you put on when you read it.

In real life: The stopwatch started at midnight UTC on January 1, 1970
In JavaScript: Timestamp 0
In real life: Every tick is one millisecond
In JavaScript: The number stored by Date
In real life: People in different cities read the same moment on different clocks
In JavaScript: UTC and local getters show different labels

Where the analogy stops: Real stopwatches do not know calendars. Date has calendar methods, and those methods inherit old API choices such as zero-based months.

Definition

A JavaScript Date represents one instant in time as a millisecond timestamp. Its methods either read that instant in UTC, read it in your local time zone, mutate it, or format it for storage or display.

Timestamps & Date.now()

INTERACTIVE

Date.now() returns the current timestamp. Because “current” depends on when the reader loads the page, the playground below computes it only after the page mounts and labels the local results in your time zone. The same instant is shown several ways.

One moment, many clocks
Live clock codePop out in the code editor (opens in a new tab)JavaScript
const now = Date.now();const instant = new Date(now);instant.toISOString();instant.toString();instant.toLocaleString();instant.toLocaleString("en-US", { timeZone: "Asia/Tokyo" });
Same instantin your time zone
Date.now()
mounting...
toISOString()
mounting...
toString()
mounting...
toLocaleString()
mounting...
UTC
mounting...
America/New_York
mounting...
Asia/Kolkata
mounting...
Asia/Tokyo
mounting...
Try it yourself

Waiting until the page mounts avoids a server/browser time mismatch.

Live values are intentionally client-only and labeled in your time zone. The interval is cleaned up when the playground unmounts.

new Date(timestamp) wraps the number. toISOString() shows the same instant in UTC. toString() and the default toLocaleString() show it through your environment’s local settings. When you need deterministic output, pass an explicit locale and timeZone to Intl.DateTimeFormat or toLocaleString options.

A Date wraps one timestamp
Step 0 of 5Ready
Your turn: follow the blue line

Step through a Date as a wrapper around one timestamp. Predict the month before line 3.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
const moment = new Date(stamp);const month = moment.getUTCMonth();const iso = moment.toISOString();const number = +moment;
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
Real-life analogyDate is a pair of glasses for the same moment

The timestamp does not change when you ask for UTC or local time. Only the label changes, like two people standing in different cities describing the same video call.

In real life: The object you are looking at
In JavaScript: The timestamp
In real life: UTC glasses
In JavaScript: getUTCFullYear(), toISOString()
In real life: Local glasses
In JavaScript: getFullYear(), toString()
In real life: Locale glasses
In JavaScript: toLocaleString() and Intl

Where the analogy stops: Glasses do not change the object. Date setters do mutate the Date object, so treat setters as tools that rewrite the timestamp.

Creating & parsing dates

INTERACTIVE

There are four common ways to create dates. Use new Date() for the current instant, new Date(timestamp) when you already have milliseconds, Date.UTC(...) plus new Date(...) for deterministic components, and ISO strings for stored data.

Date creation forms
FormMeaningWatch out
new Date()The current instantClient-only in rendered lessons or UI because it changes every millisecond
new Date(1710000000000)Wrap this timestampThe number is UTC-based even if local methods display another clock time
new Date(2024, 0, 31)Local time componentsMonths are 0-based: 0 is January
new Date(Date.UTC(2024, 0, 31))UTC componentsDate.UTC returns a timestamp, not a Date object
new Date("2024-03-10T09:00Z")Parse an ISO instantInclude Z or an offset when the instant matters

Parsing is where old Date hurts. ISO strings are specified; many natural-language or slash formats are implementation-dependent. Try the lab, then notice which rows say UTC, local, offset, or invalid.

Parsing lab: which string becomes which instant?
Strings to testPop out in the code editor (opens in a new tab)JavaScript
new Date("2024-03-10");          // date-only ISO: UTCnew Date("2024-03-10T09:00");    // no offset: local timenew Date("2024-03-10T09:00Z");   // Z: UTCnew Date("2024-03-10T09:00+05:30");new Date("March 10, 2024");      // non-ISO: avoid for datanew Date("not a date").getTime(); // NaN
Parsed resultin your time zone
Try it yourself

The lab waits until mount before parsing strings whose result can depend on the reader's time zone.

Use ISO strings for data. Date-only ISO is UTC; date-time without an offset is local time; non-ISO strings are not portable.
Important parse rules
  • "2024-03-10" is a date-only ISO string, so it parses as midnight UTC.
  • "2024-03-10T09:00" has a time but no offset, so it parses as 9:00 in your time zone.
  • "2024-03-10T09:00Z" and "2024-03-10T09:00+05:30" name exact instants.
  • Invalid dates produce a Date whose getTime() is NaN.

ISO 8601, Date.UTC & time zones

SORT

ISO 8601 is the family of date formats like 2024-03-10T09:00:00.000Z. The Z suffix means UTC. An offset such as +05:30 means “this local clock time in a place five and a half hours ahead of UTC.” Both define a single instant.

UTC, local, or locale formatting?
  • date.toISOString()
  • Date.UTC(2024, 0, 31)
  • new Date("2024-03-10")
  • new Date("2024-03-10T09:00")
  • date.getHours()
  • date.setMonth(1)
  • date.toLocaleString()
  • Intl.DateTimeFormat("en-GB", { timeZone: "UTC" })
Try it yourself
0 of 8 correct

Sort each Date method or string by what it depends on.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Date.UTC(year, monthIndex, day, hour) is a reliable way to construct a timestamp from components. Remember the month index: January is 0, December is 11. It returns a number; wrap it with new Date(...) when you want Date methods.

Getters, setters & overflow

STEP THROUGH

Dates have local getters such as getFullYear(), getMonth(), getDate(), getDay(), and getHours(). They also have UTC versions: getUTCFullYear(), getUTCMonth(), and so on. The UTC versions are best for tests because they do not depend on the machine’s time zone.

Component gettersPop out in the code editor (opens in a new tab)JavaScript
const jan31 = new Date(Date.UTC(2024, 0, 31, 9));jan31.getUTCFullYear(); // 2024jan31.getUTCMonth();    // 0, Januaryjan31.getUTCDate();     // 31

Setters mutate the existing Date. They also overflow rather than clamp. If you ask for “February 31,” Date rolls into March. That is sometimes useful, sometimes a bug.

Setters overflow instead of clamping
Step 0 of 4Ready
Your turn: follow the blue line

Watch setters mutate the same Date object and allow overflow. UTC methods keep this replay deterministic.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
due.setUTCMonth(due.getUTCMonth() + 1);due.setUTCDate(due.getUTCDate() + 7);console.log(due.toISOString());
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
Mutation warning

If two variables point at the same Date object, setMonth or setDate changes what both variables see. Copy first with new Date(oldDate.getTime()) when you need the original.

Date arithmetic

For exact instants, subtract timestamps. The result is milliseconds. Dividing by 86_400_000 gives 24-hour chunks. That is perfect for UTC timestamps like API expiry times, but it is not always the same as “calendar days in a local time zone” because daylight saving time can make local days 23 or 25 hours long.

Comparing and subtracting datesPop out in the code editor (opens in a new tab)JavaScript
const a = new Date("2024-06-01T00:00:00Z");const b = new Date("2024-06-01T00:00:00Z");console.log(a === b);console.log(a.getTime() === b.getTime());console.log(a < new Date("2024-06-02T00:00:00Z"));

Two Date objects are objects, so === checks whether they are the same object, not whether they represent the same instant. Use getTime(), unary +date, or relational comparisons like < when you mean the timestamp.

Adding days with UTC settersPop out in the code editor (opens in a new tab)JavaScript
const due = new Date(Date.UTC(2024, 0, 31, 9));due.setUTCMonth(due.getUTCMonth() + 1);due.setUTCDate(due.getUTCDate() + 7);console.log(due.toISOString());
Temporal teaser

Much of this pain is why JavaScript is getting Temporal, the modern replacement covered next. Temporal separates exact instants, plain calendar dates, time zones, and durations instead of forcing one old object to do all of them.

toISOString, toJSON & toLocaleString

Use different output for machines and humans. toISOString() is for storage and APIs: always UTC, always a Z suffix. toJSON() calls toISOString(), which is why JSON turns Date objects into ISO strings. toLocaleString(), toLocaleDateString(), and Intl.DateTimeFormat are for people.

Storage format vs audience formatPop out in the code editor (opens in a new tab)JavaScript
const instant = new Date("2024-12-24T18:30:00Z");console.log(instant.toISOString());console.log(instant.toJSON());console.log(new Intl.DateTimeFormat("en-GB", {  weekday: "long",  day: "numeric",  month: "long",  year: "numeric",  timeZone: "UTC"}).format(instant));

For UI, pass the locale and options on purpose: en-GB and en-US order dates differently; weekday and month: "long" make labels friendlier. The Internationalization with Intl lesson goes much deeper into formatting dates, numbers, lists, and plurals.

Where you’ll use this

Date work appears in forms, dashboards, calendars, cache expiration, logs, and API data. A practical workflow is: store an ISO string, revive or wrap it when you need Date methods, calculate with timestamps or UTC methods, then format at the edge for a specific audience.

Practical Date choices
JobGood defaultWhy
Store in JSONISO string from toISOString()Stable UTC text that survives systems and time zones
Show to a userIntl.DateTimeFormat(locale, options)People expect their local language and date order
Compare expiry timesgetTime() or <You are comparing instants
Add calendar days in testsUTC getters/settersAvoid machine-local time zone surprises
Schedule future app featuresTemporalIt models dates, times, zones, and durations separately

Pitfalls & misconceptions

“Months are 1-based.”

Date component months are 0-based. new Date(2024, 0, 31) is January 31.

“All ISO-looking strings are UTC.”

Date-only ISO strings are UTC, but date-time strings without an offset are local time.

“Invalid dates throw immediately.”

Usually they do not. You get Invalid Date, and getTime() returns NaN.

“Adding one month clamps to the last day.”

Setters overflow. January 31 plus one month rolls into March, with local results affected by the year and time zone.

“toLocaleString is good for tests.”

Only when you pass explicit locale and timeZone. Otherwise it depends on the environment.

“Two matching Date objects are ===.”

They are separate objects. Compare timestamps.

Date pitfalls and fixes
ProblemSurpriseSafer habit
Month numbersJanuary is 0Name variables monthIndex, or use ISO strings
ParsingNon-ISO variesAccept ISO only for data
Local mathDST changes hour countsUse UTC for fixed 24-hour chunks; use Temporal for calendar math
FormattingDefaults differ by readerPass locale and timeZone

Practice: dates without surprises

5 EXERCISES
Exercise 1 · Warm-upPredict the month index

Predict the output. This checks the old building “ground floor is 0” rule.

Starter codePop out in the code editor (opens in a new tab)JavaScript
const birthday = new Date(2024, 0, 31);
console.log(birthday.getMonth());

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeCompute days between fixed UTC instants

    Work out the number of days without running it first.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const start = new Date("2024-06-01T00:00:00Z");
    const end = new Date("2024-06-11T00:00:00Z");
    console.log((end - start) / 86400000);

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeFormat for UK and US audiences

      Run the code and compare the two lines. Then change dateStyle to "medium".

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      const instant = new Date("2024-12-24T18:30:00Z");
      console.log(new Intl.DateTimeFormat("en-GB", { dateStyle: "full", timeZone: "UTC" }).format(instant));
      console.log(new Intl.DateTimeFormat("en-US", { dateStyle: "full", timeZone: "UTC" }).format(instant));
        Exercise 4 · PracticeFix the December bug

        The code accidentally creates a date in the next year. What month number should December use?

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        // Bug: intended December 5, 2024
        const launch = new Date(2024, 12, 5);
        console.log(launch.toISOString());

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeCompare two dates correctly

          Why does this print true even though two separate Date objects were created?

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          const a = new Date("2024-06-01T00:00:00Z");
          const b = new Date("2024-06-01T00:00:00Z");
          console.log(a.getTime() === b.getTime());

          Answer, then press Check. Spacing and letter case don’t matter.

            Quiz: check your understanding

            7 QUESTIONS
            Lesson quiz · 7 questionsScore: first tries count
            1. Question 1 of 7What does a JavaScript Date object store internally?

              Choose an answer to see the explanation.

            2. Question 2 of 7What month number does this print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log(new Date(Date.UTC(2024, 0, 31)).getUTCMonth());

              Choose an answer to see the explanation.

            3. Question 3 of 7Which string is parsed as midnight UTC?

              Choose an answer to see the explanation.

            4. Question 4 of 7What does this invalid-date check print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const d = new Date("not a date");
              console.log(Number.isNaN(d.getTime()));

              Choose an answer to see the explanation.

            5. Question 5 of 7What type does Date.UTC return here?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const stamp = Date.UTC(2024, 0, 1);
              console.log(typeof stamp);

              Choose an answer to see the explanation.

            6. Question 6 of 7How should you compare two Date objects for the same instant?

              Choose an answer to see the explanation.

            7. Question 7 of 7Which formatter always uses UTC and includes a Z suffix?

              Choose an answer to see the explanation.

            Key takeaways

            • A Date stores one millisecond timestamp; methods choose how to read it.
            • Date.now() and Date.UTC() return numbers. new Date(...) returns an object.
            • Months are 0-based in component constructors and setters.
            • Date-only ISO strings parse as UTC; date-time strings without offsets parse in local time.
            • Use UTC methods and explicit timeZone options for deterministic tests.
            • toISOString() is for machines; Intl.DateTimeFormat and locale methods are for people.

            Remember the one-liner.
            A Date is one timestamp read through UTC, local, or locale-specific glasses.

            Up next: Temporal, the modern replacement that fixes many of Date’s pain points.

            CompleteFrontend Clear concepts. Working examples.