completefrontend
Open lab My progress
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 timestampsExplain that a Date holds one number: the milliseconds since the start of 1970 in UTC.
  • 02
    Read date text safelyUse ISO strings, and know which ones mean UTC and which mean your local time.
  • 03
    Do date math and show dates on purposeUse UTC methods for math that gives the same result on every computer, and Intl to show dates to people.

One number, many clocks

People think about dates in many ways: birthdays, deadlines, calendars, and time zones. A JavaScript Date is much simpler. Inside, it keeps just one number.

That number is called a timestamp. It counts milliseconds (thousandths of a second) from one fixed moment: midnight at the start of January 1, 1970, in UTC. You will see that moment written as 1970-01-01T00:00:00.000Z.

UTC stands for Coordinated Universal Time. It is the world's reference clock. It never moves for summer time. Every other time zone is described as a number of hours ahead of UTC or behind it.

Everything else a Date can do is a way to read that one number, change it, or write it as text.

One Date, three clocks. A box called moment holds one timestamp, 1706702400000. Arrows lead to three clock times for that same moment: 12:00 in UTC, 17:30 in Kolkata, and 07:00 in New York.1706702400000momentone timestamp12:00UTC17:30Kolkata07:00New York
The Date keeps only the number. It is January 31, 2024, at 12:00 UTC. Each clock time is a different way to read that one number. The moment stays the same; only the clock time you see changes.

This lesson uses ideas from earlier lessons:

  • A timestamp is a plain number.
  • JSON.stringify turns a Date into text, and JSON.parse can turn that text back into a Date. You saw both in the JSON lesson.
  • +date turns a Date into its timestamp. That uses object to primitive conversion.
  • < and > work on dates because dates can turn into numbers, as in the Comparisons lesson.
Real-life analogyA timestamp is one stopwatch for the whole world

Picture one stopwatch for the whole world. It started at midnight UTC on January 1, 1970. It is the same stopwatch in Kolkata, Mumbai, New York, and Tokyo. It only counts milliseconds. A Date object holds one reading from that stopwatch.

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 a Date stores
In real life: People in different cities say different clock times for the same moment
In JavaScript: UTC methods and local methods show different hours
Definition

A JavaScript Date is an object that holds one moment in time as a timestamp: a number of milliseconds. Its methods (functions that belong to the object) do four kinds of jobs:

  • read the moment in UTC,
  • read the moment in your own time zone,
  • change the moment,
  • write the moment as text, for a computer or for a person.

Timestamps and Date.now()

INTERACTIVE

Date.now() gives you the timestamp for right now. It is a plain number, not a Date object.

"Right now" is different for every reader. So the playground below works it out in your browser, after the page has loaded. It shows the same moment in several ways. The results marked in your time zone use your computer's clock settings.

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 momentin your time zone
Date.now()
loading...
toISOString()
loading...
toString()
loading...
toLocaleString()
loading...
UTC
loading...
America/New_York
loading...
Asia/Kolkata
loading...
Asia/Tokyo
loading...
Try it yourself

Waiting for the page to load. The server and your browser could show different times, so only your browser makes these values.

These live values are made only in your browser, and they are marked “in your time zone”. The clock stops ticking when this playground leaves the page.

Here is what the playground code does:

  • new Date(now) puts the timestamp inside a Date object.
  • toISOString() writes the moment as text in UTC.
  • toString() and toLocaleString() write the moment using your computer's time zone and language settings.
  • The last line passes a language, "en-US", and a timeZone. So it shows Tokyo time in US English on every computer.

When you need the same output on every computer, choose the language and the time zone yourself. Both toLocaleString and Intl.DateTimeFormat accept them as options.

JavaScript also has a second clock. The code below uses both clocks.

Two clocks: Date.now() and performance.now()Pop out in the code editor (opens in a new tab)JavaScript
const savedAt = Date.now();const expiresAt = savedAt + 5000;console.log(expiresAt - savedAt); const started = performance.now();const finished = started + 500;console.log(finished > started);
  • Lines 1 to 3 use Date.now(). It tells you when something happened, like the time on a wall clock. Use it for times you save, such as "this link expires at …". Line 2 adds 5000 milliseconds (5 seconds), so line 3 prints 5000.
  • Lines 5 to 7 use performance.now(). It counts milliseconds since this page opened. Use it to measure how long a task took. Line 6 adds 500, so line 7 prints true.

The example adds fixed numbers so its output is always the same. In real code, you read the clock once before the work and once after it, then subtract.

Now step through a small program. It makes a timestamp, puts it in a Date, and then reads it back in three ways.

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

Step through a Date that holds one timestamp. Before line 3 runs, guess which month number it gives.

Running in
  1. script
Next: line 1
const moment = new Date(stamp);const month = moment.getUTCMonth();const iso = moment.toISOString();const number = +moment;
CallStoreChangeResultBlue line: runs next. 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 analogyDifferent glasses for the same moment

Put on different glasses and the same thing looks different. The thing itself does not change. In the same way, the timestamp does not change when you ask for UTC or for local time. Only the words and numbers you see change. Think of two friends in different cities on one video call. One says it is 9 in the morning. The other says it is 7:30 in the evening. It is still the same call.

In real life: The thing you look 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: Language glasses
In JavaScript: toLocaleString() and Intl

Creating and parsing dates

INTERACTIVE

There are four common ways to create a Date:

  • new Date() for the moment right now.
  • new Date(timestamp) when you already have the milliseconds.
  • new Date(Date.UTC(...)) when you have a year, a month, a day, and so on, and want them read as UTC. The result is the same on every computer.
  • new Date("...") with an ISO string, for dates you saved as text.

An ISO string is date text in one standard shape, like "2024-03-10T09:00Z": the year, month, and day, then a T, then the time. The next section explains each part.

Each row of the table shows one way to create a Date. Read across: the code, what you get, and what to watch for.

Ways to create a Date
When you writeWhat you getWatch out for
new Date()The moment right now.It changes every millisecond, so a page should make it in the browser, not on the server.
new Date(1710000000000)A Date that holds this timestamp.The number counts from UTC, even when local methods show a different clock time.
new Date(2024, 0, 31)January 31, 2024, at midnight in your time zone.Months start at 0, so 0 is January.
new Date(Date.UTC(2024, 0, 31))January 31, 2024, at midnight UTC.Date.UTC gives back a number, not a Date. new Date puts it in a Date.
new Date("2024-03-10T09:00Z")9:00 UTC on March 10, 2024.Add Z or an offset like +05:30 (hours ahead of UTC) when the exact moment matters.

Parsing means reading text, like "2024-03-10", and turning it into a Date. Parsing is where Date causes the most trouble. The rules for ISO strings are written down in the JavaScript standard. Other shapes, like "March 10, 2024" or "10/03/2024", have no fixed rules. Each browser may read them its own way.

Try the lab. Click each string. See whether it becomes a UTC time, a local time, a time with an offset, or an invalid date.

Parsing lab: which string becomes which moment?
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 for the page to load, because some results depend on your time zone.

Use ISO strings for data. A date-only ISO string is UTC. A date and time with no offset is local time. Other shapes may be read differently in different browsers.

The next example puts a date-only string next to a string with a date and a time.

A date-only string is UTC; a date and time is localPop out in the code editor (opens in a new tab)JavaScript
const dateOnly = new Date("2024-03-10");const meeting = new Date("2024-03-10T09:00");const localComponents = new Date(2024, 2, 10, 9);console.log(dateOnly.toISOString());console.log(meeting.getTime() === localComponents.getTime());
  1. Line 1 has a date only. A date-only ISO string means midnight UTC.
  2. Line 2 has a date and a time, but no Z and no offset. So it means 9:00 in your time zone.
  3. Line 3 builds 9:00 on March 10 from numbers. Numbers passed to new Date are also read in your time zone. Month 2 is March.
  4. Line 4 prints 2024-03-10T00:00:00.000Z on every computer.
  5. Line 5 prints true on every computer. Lines 2 and 3 mean the same local moment, so their timestamps match.

The code doesn't print meeting.toISOString(). That UTC text would be different in each time zone.

Parsing rules to remember
  • "2024-03-10" has a date only. It means midnight UTC.
  • "2024-03-10T09:00" has a time but no offset. It means 9:00 in your time zone.
  • "2024-03-10T09:00Z" and "2024-03-10T09:00+05:30" each name one exact moment.
  • Text that isn't a date still gives a Date object. Its getTime() is NaN ("not a number").

ISO strings, Date.UTC and time zones

SORT

ISO 8601 is the name of the standard for date text like 2024-03-10T09:00:00.000Z. Read it from left to right:

  • 2024-03-10 is the year, the month, and the day.
  • T separates the date from the time.
  • 09:00:00.000 is hours, minutes, seconds, and milliseconds.
  • Z means the time is in UTC.

Instead of Z, a string can end with an offset, like +05:30. It means "this clock time, in a place 5 hours and 30 minutes ahead of UTC". India uses that offset. A Z or an offset makes the string name one exact moment.

UTC, local time, or written for a language?
  • 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 method or string by what its result 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) builds a timestamp from parts and reads them as UTC. The result is the same on every computer. Remember two things:

  • The month is an index that starts at 0. January is 0 and December is 11.
  • It gives back a number. Wrap it in new Date(...) when you want Date methods.
Month indexes. Index 0 is January, 1 is February, and 11 is December. Index 12 is too big, so it rolls over to January of the next year.month index0January1February…11Decembertoo big12Jan 2025
Date counts months from 0, so December is 11. There is no month 12. If you pass 12, the Date rolls over to January of the next year. Exercise 4 has this bug.

Getters, setters and overflow

STEP THROUGH

Here, a getter is a method that reads one part of a date, like the year or the hour. Dates have two sets of getters:

  • Local getters read the date in your time zone: getFullYear(), getMonth(), getDate() (the day of the month), getDay() (the day of the week, with Sunday as 0), and getHours().
  • UTC getters read the date in UTC: getUTCFullYear(), getUTCMonth(), and so on.

Use the UTC getters in tests. Their results don't depend on the computer's time zone.

Read the month and the day in UTCPop out in the code editor (opens in a new tab)JavaScript
const jan31 = new Date(Date.UTC(2024, 0, 31, 9));console.log(jan31.getUTCMonth()); // 0, Januaryconsole.log(jan31.getUTCDate());  // 31

Line 1 makes January 31, 2024, at 9:00 UTC. Line 2 prints 0, the month index for January. Line 3 prints 31, the day of the month.

A setter is a method that changes one part, like setMonth() or setUTCDate(). A setter changes the Date object itself. It doesn't make a new one.

Setters also overflow: if the day you ask for doesn't exist, the extra days carry over into the next month. Ask for February 31 and you get a day in March. Sometimes that helps. Sometimes it is a bug.

Setter overflow. January 31 plus one month asks for February 31. That day doesn't exist, so the extra days carry over, and the date becomes March 2.Jan 31start+1 monthFeb 31no such daycarry overMar 22 days past Feb 29
In 2024, February has 29 days. February 31 would be 2 days past the end of February, so the Date lands on March 2. Setters carry the extra days over like this. They don't stop at the last day of the month.

Step through the code below and watch one Date change.

A setter carries extra days into the next month
Step 0 of 4Ready
Your turn: follow the blue line

Watch setters change the same Date object. A day that doesn't exist carries over into the next month. UTC methods make the result the same on every computer.

Running in
  1. script
Next: line 1
due.setUTCMonth(due.getUTCMonth() + 1);due.setUTCDate(due.getUTCDate() + 7);console.log(due.toISOString());
CallStoreChangeResultBlue line: runs next. 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.
Setters change the original

If two variables point to the same Date object, setMonth or setDate changes what both of them see. When you need to keep the original, make a copy first with new Date(oldDate.getTime()).

Copy before changing a DatePop out in the code editor (opens in a new tab)JavaScript
const original = new Date(Date.UTC(2024, 0, 31));const copy = new Date(original.getTime());copy.setUTCDate(copy.getUTCDate() + 1);console.log(original.toISOString());console.log(copy.toISOString());
  1. Line 1 makes January 31, 2024, at midnight UTC.
  2. Line 2 makes a new Date from the original's timestamp. It is a separate object, not a second name for the same one.
  3. Line 3 adds one day to the copy only.
  4. Line 4 prints January 31 for the original.
  5. Line 5 prints February 1 for the copy.

Doing math with dates

To find the time between two exact moments, subtract one Date from the other. The result is in milliseconds. Divide it by 86_400_000, the number of milliseconds in 24 hours, to get days.

That works well for UTC times, like the moment a login expires. It doesn't always match calendar days in your own time zone. Many places move their clocks for summer time (this is called daylight saving time). On those days, a local day is 23 or 25 hours long.

Compare two 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"));
  1. Line 1 makes a Date for June 1, 2024, at midnight UTC.
  2. Line 2 makes a second Date for the same moment.
  3. Line 3 prints false. === checks whether a and b are the very same object. They are two separate objects.
  4. Line 4 prints true. getTime() gives each timestamp, and the two numbers are equal.
  5. Line 5 prints true. < turns both dates into timestamps, and June 1 comes before June 2.
Two ways to compare. Left: a === b compares two separate Date objects, so it is false. Right: a.getTime() === b.getTime() compares their two timestamps, which are both 1717200000000, so it is true.a === bJune 1object aJune 1object bfalsea.getTime() === b.getTime()1717200000000number of a1717200000000number of btrue
a and b are two separate objects for the same moment. === asks "Is this the same object?" and the answer is no. Comparing timestamps asks "Is this the same moment?" and the answer is yes.

So when you mean "the same moment", compare timestamps with getTime() or +date. To measure a time span, you subtract those same timestamps.

You can also move a date forward with UTC setters. This is the same code you stepped through earlier.

Add a month and a week 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());

Line 2 adds one month, and line 3 adds seven days. Line 4 prints 2024-03-09T09:00:00.000Z on every computer, because every step uses UTC.

Coming next: Temporal

Many of these problems are why JavaScript is getting Temporal, a newer set of date tools. The next lesson covers it. Temporal has a separate type for each of these:

  • an exact moment,
  • a calendar date with no time,
  • a time zone,
  • a length of time, such as 3 days.

Date tries to do all of these with one object.

Turning dates into text

Write dates one way for computers and another way for people:

  • toISOString() is for saving and sending data. It always uses UTC and always ends with Z.
  • toJSON() calls toISOString(). That is why JSON.stringify turns a Date into an ISO string.
  • toLocaleString(), toLocaleDateString(), and Intl.DateTimeFormat are for people. They write the date in a person's language and style.
One format for computers, one for peoplePop 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));console.log(instant.toLocaleDateString("en-GB", { timeZone: "UTC" }));
  1. Line 1 makes a Date for December 24, 2024, at 18:30 UTC.
  2. Lines 2 and 3 print the same ISO text: 2024-12-24T18:30:00.000Z.
  3. Lines 4 to 10 make a formatter with Intl.DateTimeFormat. "en-GB" means British English. The options ask for the weekday and the month as words, and for UTC. It prints Tuesday, 24 December 2024.
  4. Line 11 uses toLocaleDateString with British English and UTC. It prints 24/12/2024 on every computer.

For pages people read, choose the language and the options on purpose. en-GB puts the day first, and en-US puts the month first. The Internationalization with Intl lesson covers formatting dates, numbers, lists, and plurals in depth.

Where you'll use this

Dates show up in forms, dashboards, calendars, logs, and data from APIs. They also decide when saved data is too old to use. A good habit has four steps:

  1. Save the moment as an ISO string.
  2. Turn it back into a Date when you need Date methods.
  3. Do math with timestamps or UTC methods.
  4. Turn it into text for people only at the end, when you show it.

For calendar dates, named time zones, and lengths of time, move on to Temporal instead of writing more helper code around Date.

Each row of the table is a common job. Read across to see a good first choice and why.

Good choices for common date jobs
The jobA good first choiceWhy
Save a date in JSONAn ISO string from toISOString().It is UTC text that reads the same on every computer.
Show a date to a personIntl.DateTimeFormat(locale, options).People expect their own language and date order.
Check whether something has expiredgetTime() or <.You are comparing two exact moments.
Add calendar days in testsUTC getters and setters.The computer's time zone can't change the result.
Build new date featuresTemporal.It has separate types for dates, times, time zones, and lengths of time.

Common mistakes with dates

"Months are numbered from 1."

In Date, months are numbered from 0. new Date(2024, 0, 31) is January 31.

"Every ISO string is UTC."

A date-only ISO string is UTC. A string with a date and a time but no offset is local time.

"A bad date string throws an error."

Usually it doesn't. You get an Invalid Date object, and its getTime() returns NaN.

"Adding a month stops at the last day of the month."

Setters overflow. January 31 plus one month asks for February 31, which carries over into March. The exact day depends on the year. With local setters, it can also depend on the time zone.

"toLocaleString is fine for tests."

Only when you pass a language and a timeZone. Without them, the result depends on the computer.

"Two Dates for the same moment are ===."

They are two separate objects, so === gives false. Compare their timestamps.

Each row of the table is one problem. Read across to see what surprises people and a safer habit.

Date problems and how to avoid them
ProblemWhat surprises peopleA safer habit
Month numbersJanuary is 0.Name the variable monthIndex, or use ISO strings.
Parsing textText that isn't ISO is read differently in different browsers.Accept only ISO strings for data.
Math in local timeSummer time changes make some days 23 or 25 hours long.Use UTC for exact 24-hour days. Use Temporal for calendar math.
FormattingThe default output is different for each reader.Pass a language and a timeZone.

Practice: dates without surprises

5 EXERCISES
Exercise 1 · Warm-upPredict the month index

Predict what line 2 prints. Remember how Date numbers its months.

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 · PracticeCount the days between two UTC dates

    Work out the number of days before you run the code.

    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 readers

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

      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

        This code should make December 5, 2024, but it makes a date in the next year. Which 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 it makes two separate Date objects?

          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 keep inside?

              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 means midnight UTC?

              Choose an answer to see the explanation.

            4. Question 4 of 7What does this check for an invalid date 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 give back 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 check whether two Date objects are the same moment?

              Choose an answer to see the explanation.

            7. Question 7 of 7Which method always uses UTC and ends with a Z?

              Choose an answer to see the explanation.

            Key takeaways

            • A Date holds one timestamp in milliseconds. Its methods choose how to read it.
            • Date.now() and Date.UTC() give back numbers. new Date(...) gives back an object.
            • Months start at 0 when you pass numbers to new Date, Date.UTC, and setters.
            • A date-only ISO string is UTC. A string with a date and a time but no offset is local time.
            • For results that are the same on every computer, use UTC methods and pass a timeZone.
            • toISOString() is for computers. Intl.DateTimeFormat and the toLocale methods are for people.

            Remember the one-liner.
            A Date is one timestamp. You can read it in UTC, in your local time, or written for a person's language.

            Up next: Temporal, newer date tools that fix many of Date's problems.

            CompleteFrontend Clear concepts. Working examples.