Learn by reading through in order

Timers and Dates — setTimeout / Date / Intl

Call functions later with setTimeout, stop setInterval, handle Date's zero-based months, format dates with Intl.DateTimeFormat, and count days between dates.

Code that simply runs from top to bottom can't close a notification after 3 seconds or count down the time left once a second. And as long as a date is just a string, you can't show which day of the week it falls on or count the days left until a deadline.

This article covers setTimeout and setInterval, which set up timers, and Date and Intl.DateTimeFormat, which work with dates and times.

Scheduling a Call for Later — setTimeout and clearTimeout

Say a photo-sharing site shows an "Upload complete" notification, and you want to close it after 3 seconds. Code that runs from top to bottom has no way to say "wait 3 seconds, then run the next part", and if you kill time with an empty for loop, the page stops responding to clicks and stops updating until the loop finishes.

setTimeout(function, milliseconds) only schedules the call; execution moves straight on to the next line. It returns a timer ID, the same kind you used for the fetch timeout, and passing that ID to clearTimeout cancels the call.

// Returns a Promise that's fulfilled after ms milliseconds (the delay from the Promise article, without the value argument)
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

// Schedule closing the notification in 100ms, then move on right away (about 3000ms on a real page)
setTimeout(() => console.log("Notification closed"), 100);   // Notification closed (2nd)
console.log("Upload complete");                              // Upload complete (1st)

// The user left the page, so cancel the recommendations. This never runs, even after 100ms
const recommendTimer = setTimeout(() => console.log("Showing recommended photos"), 100);
clearTimeout(recommendTimer);

// Wait here 200ms before moving on
await delay(200);
console.log("Waited 200ms");                                 // Waited 200ms (3rd)
The Order Written Isn't the Order Printed
0msUpload completePrinted 1stsetTimeout lineprints nothing0msRecommendationscanceledNever called,even at 100ms100msNotificationclosedPrinted 2nd,during the await200msWaited 200msLine after awaitprints 3rd
The setTimeout line prints nothing, and the canceled call never runs. The scheduled function runs at the 100ms mark, while await is waiting.

A scheduled function can run after the last line of your code has already finished. To see its output in the exercise console, end your code with a line that waits, such as await delay(…), as you did in the Promise article.

In an email app, hold outgoing mail for a moment so it can still be canceled. delay is already declared.

① Schedule printing "Email 1 sent" in 150ms, wait 50ms, and cancel it.

② Print "Canceled sending email 1".

③ Schedule printing "Email 2 sent" in 100ms, wait 150ms, and cancel it.

④ Print "Couldn't cancel email 2".

(An explanation appears once your code runs correctly.)

JavaScript / TypeScript Editor

Run code to see output

Stopping a Repeat — setInterval and clearInterval

Say you want to show the seconds left until a sale ends, counting down once per second. setTimeout calls the scheduled function only once, so to repeat it, the function would have to schedule itself again on every call, with the stop condition tangled up in that same code.

setInterval (a function that calls another function over and over at a fixed interval in milliseconds) keeps calling it until you pass the timer ID it returned to clearInterval.

To run the next line only after the repetition ends, wrap the whole thing in a Promise and call resolve right after clearInterval, inside the same if.

// Count down the seconds left by 1 every 100ms (every 1000ms on a real page)
function startCountdown(seconds) {
  return new Promise((resolve) => {
    let remaining = seconds;                  // Declared outside the callback, so it keeps its value between calls
    const timerId = setInterval(() => {
      remaining -= 1;
      console.log(`Time left: ${remaining}s`);   // Time left: 2s → Time left: 1s → Time left: 0s
      if (remaining === 0) {
        clearInterval(timerId);               // Stop on the call that hits 0 (timerId is already assigned by then)
        resolve();                            // Let the await line know it's done
      }
    }, 100);
  });
}

await startCountdown(3);
console.log("The sale has ended");            // The sale has ended
Calling resolve Lets await Move On
Caller — the await startCountdown(3) line
  • Doesn't move to the next line until the returned Promise is fulfilled
  • The sale has ended prints after resolve
Inside startCountdown(3) — runs once
  • let remaining = seconds — created only once here, holding 3
  • const timerId = setInterval(...) — the ID to pass when stopping
Function called every 100ms — runs 3 times
  • remaining -= 1 — decrements the outer variable: 2 → 1 → 0
  • On the call that hits 0, clearInterval(timerId) and resolve()
The outer variable remaining is created only once and goes down by 1 on each call. Calling resolve on the call that reaches 0 lets the caller's await move on.

If you move let remaining = seconds inside the callback, it starts over at 3 on every call and never gets below 2, so neither clearInterval nor resolve is ever called. After 15 seconds, the exercise console gives up and reports a timeout.

An Interval You Never Stop Leaks into Later Runs

In this article, the exercise console runs every exercise on the same page. If a run ends without calling clearInterval, its callback keeps getting called until you reload the page, and its output leaks into the results of any later exercise that waits with await, so even correct code is marked wrong. If that happens, reload the article page.

Read a warehouse's stock count every 100ms and stop after 3 readings. The watchStock skeleton, the array of stock counts readings, and delay are already declared.

① Every 100ms, read the next value in readings and print it in the form "Reading 1: 12 in stock".

② Stop after the 3rd reading and fulfill the Promise.

③ Wait for it to finish, then wait another 200ms to confirm no 4th reading appears.

④ Print "Finished checking stock".

JavaScript / TypeScript Editor

Run code to see output

Turning a Date into Display Text — getMonth and Intl

Say a video list shows each publish date in the form "Wed, September 2, 2026". If you slice the month out of an API string like "2026-09-02T09:00:00", you get the string "09", and since the day of the week isn't written anywhere, you can't work it out from the string alone.

Date (a built-in object that represents a date and time) reads "2026-09-02T09:00:00" as 9:00 AM in the device's time zone (a region's local time, defined by its offset from the reference time UTC), and its get methods return the month, the day of the week, and so on as numbers.

Intl.DateTimeFormat (a class that formats dates and times as strings for a given language) takes a locale such as "en-US" (US English) when you create it.

const publishedAt = new Date("2026-09-02T09:00:00");

// Methods starting with get return numbers. For months, January is 0; for days of the week, Sunday is 0
console.log(publishedAt.getMonth(), publishedAt.getDate(), publishedAt.getDay());   // 8 2 3

// Create a formatter by passing a language and the style for each part to show
const dateFormat = new Intl.DateTimeFormat("en-US", {
  year: "numeric",     // Year as a number
  month: "long",       // Month as a name, like "September"
  day: "numeric",      // Day as a number
  weekday: "short",    // Day of the week as a short name
});
console.log(dateFormat.format(publishedAt));   // Wed, September 2, 2026
console.log(new Intl.DateTimeFormat("en-US", { hour: "numeric", minute: "2-digit" }).format(publishedAt));   // 9:00 AM

// A string without a time is read as midnight UTC
const dateOnly = new Date("2026-09-02");
console.log(dateOnly.getDate());   // 2 in Japan, 1 in New York (8:00 PM the day before)
Leave Out the Time and You Get Midnight UTC
"2026-09-02T09:00:00"Read as 9:00 AMlocal timeIn New York, too:Sep 2, 9:00 AMgetDate() is 2same day as Tokyo"2026-09-02"time omittedRead as0:00 UTCIn New York:Sep 1, 8:00 PMgetDate() is 1the day before
With the time included, the string means 9:00 AM on September 2 in whatever time zone the device uses. Leave out the time and it's read as UTC, which can land on the previous day, depending on the offset.

In time zones ahead of UTC, such as Japan, you still get 2, so the problem is easy to miss. When an API sends a date-only string, append "T00:00:00" before passing it to new Date so it's read as midnight local time. The table below lists what the get methods return for publishedAt.

MethodFor publishedAtFor display
getFullYear()2026Use directly
getMonth()8January is 0; add 1 to get 9
getDate()2Starts at 1; use directly
getDay()3Sunday is 0, so 3 is Wed
getHours()924-hour clock, 0–23

On a dental clinic's booking page, format the appointment date and time for display. reservedAt, the appointment date and time, is already declared.

① Using get methods, print the appointment date in the form "10/5".

② Create a US English formatter that shows the month, day, and day of the week in the form "Mon, October 5".

③ Use ②'s formatter to print the appointment date.

④ Create a formatter that shows the hour and minute in the form "2:30 PM", and print the appointment time.

JavaScript / TypeScript Editor

Run code to see output

Counting the Days Until a Deadline — Subtracting Dates

Say a library site shows how many days are left until a book is due. If you subtract just the day-of-month numbers, a book borrowed on September 2 and due October 3 comes out to 3 - 2 = 1 day, because the days in between that belong to September aren't counted.

Internally, a Date is a number: the milliseconds elapsed since midnight UTC on January 1, 1970. Subtracting one Date from another gives the difference in milliseconds, so dividing by the milliseconds in one day, 24 * 60 * 60 * 1000, gives the number of days.

new Date(year, month, day) creates midnight local time on that day, and here too, months count from 0.

// Milliseconds in one day (24 hours × 60 minutes × 60 seconds × 1000)
const DAY_MS = 24 * 60 * 60 * 1000;

// Borrow date and due date. When you create them from numbers, months still count from 0
const borrowedAt = new Date(2026, 8, 2);     // September 2, 2026
const dueDate = new Date(2026, 9, 3);        // October 3, 2026

// Subtracting one Date from another gives the difference in milliseconds
const diffMs = dueDate - borrowedAt;
console.log(Math.round(diffMs / DAY_MS));    // 31

// Passing 31, a day September doesn't have, rolls over into the next month instead of throwing an error
const overflowDate = new Date(2026, 8, 31);
console.log(overflowDate.getMonth() + 1, overflowDate.getDate());   // 10 1
Three Ways to Write September 30
Days fromnew Date(2026,8,2)to the due datenew Date(2026, 8, 30)new Date(2026, 9, 30)new Date(2026, 8, 31)CreatesSeptember 30October 30month 9 = OctoberOctober 1no September 3128 days58 days30 days too many29 days1 day too many
All three are meant to set the due date to September 30. Passing 9 as the month or 31 as the day doesn't throw an error; you just get the wrong day count.

The code rounds with Math.round because of daylight saving time (moving clocks forward one hour in summer). Where it's observed, some days are 23 or 25 hours long, so the division doesn't always come out to a whole number; you might get something like 30.958… instead. In places without it, such as Japan, the division is always exact, so it's easy to forget the rounding.

On a campground booking site, calculate the number of nights in a stay. The toDate and countNights skeletons and DAY_MS are already declared.

① In toDate, take a year, month, and day (with months counted from 1) and return a Date.

② In countNights, use the difference between two Dates to return the number of nights.

③ Print the number of nights from September 28 to October 2, 2026, in the form "4 nights".

④ Print the number of nights from December 29, 2026, to January 3 of the next year in the same form.

JavaScript / TypeScript Editor

Run code to see output
QUIZ

Knowledge Check

Answer each question one by one.

Q1What happens if you declare let count = 0; inside the function passed to setInterval and try to stop it when count reaches 3?

Q2What does new Date("2026-09-02T09:00:00").getMonth() return?

Q3What does getDate() return for a Date created with new Date(2026, 8, 31)?