CodeMIRAI
0 XP

Dates and times

5 min read 12 runnable examples Quiz included

Dates are everywhere: when a post was published, how many days until an exam, a person's age, a booking calendar, "last seen 5 minutes ago". JavaScript's built-in Date object represents a single moment in time, and comes with methods to read its parts, change them, compare moments and format them for people. It has a few famous quirks, which this lesson points out as they come up.

A date is a moment, stored as a number

Inside, a Date is just the number of milliseconds since 1 January 1970, 00:00 UTC (the "Unix epoch"). Everything else (year, month, day, hour in your time zone) is calculated from that number:

JavaScript
const moon = new Date(Date.UTC(1969, 6, 20, 20, 17));   // the Moon landing, in UTC
console.log(moon.getTime());          // milliseconds since 1970 (negative: it's before)
console.log(moon.toISOString());      // the standard text form, in UTC

console.log(typeof Date.now());       // the current moment as a number

Creating dates

You writeYou get
new Date()right now
new Date(2026, 9, 31)31 October 2026, 00:00 local time (month 9 is October!)
new Date(2026, 9, 31, 14, 30)the same day at 14:30 local time
new Date("2026-10-31T14:30:00")from ISO text, local time
new Date("2026-10-31")from a date-only ISO text: midnight UTC
new Date(1798675200000)from a timestamp in milliseconds
Date.UTC(2026, 9, 31)the timestamp of a UTC moment (a number)
JavaScript
const exam = new Date(2026, 2, 14, 9, 30);     // 14 March 2026, 09:30
console.log(exam.getFullYear(), exam.getMonth(), exam.getDate(), exam.getHours(), exam.getMinutes());
console.log(exam.toDateString());

Text in other formats ("31/10/2026", "Oct 31 2026") is parsed differently by different browsers, or not at all. Only the ISO format YYYY-MM-DD (optionally THH:MM:SS) is reliable.

Reading the parts

MethodGivesFor 14 March 2026, 09:30
getFullYear()the year2026
getMonth()the month, 0 to 112
getDate()the day of the month, 1 to 3114
getDay()the day of the week, 0 = Sunday to 66 (Saturday)
getHours(), getMinutes(), getSeconds()the time9, 30, 0
getTime()milliseconds since 1970
JavaScript
const d = new Date(2026, 2, 14, 9, 30);
const days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
console.log(`${days[d.getDay()]}, day ${d.getDate()} of month ${d.getMonth() + 1}`);

const pad = (n) => String(n).padStart(2, "0");
console.log(`${pad(d.getDate())}/${pad(d.getMonth() + 1)}/${d.getFullYear()}`);   // DD/MM/YYYY
console.log(`${pad(d.getHours())}:${pad(d.getMinutes())}`);

Every getter has a UTC twin (getUTCHours, getUTCDate...) that reads the time in UTC instead of the visitor's time zone.

Formatting for people

toLocaleDateString and toLocaleTimeString format a date the way a country writes it. Always pass the locale ("en-IN", "en-US", "de-DE") if you want the same result for everyone; without it, each visitor gets their own browser's style:

JavaScript
const d = new Date(2026, 9, 31, 14, 5);
console.log(d.toLocaleDateString("en-IN"));                     // 31/10/2026
console.log(d.toLocaleDateString("en-US"));                     // 10/31/2026
console.log(d.toLocaleDateString("en-IN", { dateStyle: "long" }));
console.log(d.toLocaleDateString("en-IN", { weekday: "long", day: "numeric", month: "long", year: "numeric" }));
console.log(d.toLocaleTimeString("en-IN", { hour: "2-digit", minute: "2-digit" }));
console.log(d.toLocaleDateString("hi-IN", { dateStyle: "full" }));   // in Hindi

For formatting many dates the same way, make one formatter with Intl.DateTimeFormat and reuse it:

JavaScript
const fmt = new Intl.DateTimeFormat("en-GB", { day: "numeric", month: "short", year: "numeric" });
const dates = [new Date(2026, 0, 26), new Date(2026, 7, 15), new Date(2026, 9, 2)];
console.log(dates.map((d) => fmt.format(d)).join(" | "));

Comparing dates and measuring the gap

Dates compare as their millisecond numbers, so < and > work, and subtracting two dates gives the difference in milliseconds. === does not work: two Date objects are never the same object:

JavaScript
const a = new Date(2026, 9, 31);
const b = new Date(2026, 11, 25);
console.log(a < b, b - a);                      // b - a is in milliseconds
console.log(a === new Date(2026, 9, 31));       // false: different objects
console.log(a.getTime() === new Date(2026, 9, 31).getTime());   // compare the numbers

const DAY = 24 * 60 * 60 * 1000;                // milliseconds in a day
console.log("days between:", Math.round((b - a) / DAY));

Math.round (rather than floor) protects against days that are 23 or 25 hours long when clocks change for daylight saving.

Adding days and months

The set... methods change a date in place, and they roll over sensibly: day 32 of October becomes 1 November. That makes adding days easy:

JavaScript
function addDays(date, n) {
  const copy = new Date(date);       // don't change the original
  copy.setDate(copy.getDate() + n);
  return copy;
}
const start = new Date(2026, 9, 25);
console.log(addDays(start, 10).toDateString());     // rolls into November
console.log(addDays(start, -30).toDateString());    // and back into September

const due = new Date(2026, 0, 31);
due.setMonth(due.getMonth() + 1);                   // 31 January + 1 month...
console.log(due.toDateString());                    // ...is 3 March: February has no 31st

The last example is a classic trap: adding a month to the 31st overflows into the following month. Clamp to the month's last day if that matters.

Checking that a date is valid

An invalid date doesn't throw; it becomes Invalid Date, whose time is NaN. Impossible days are trickier: new Date(2026, 1, 30) quietly rolls over to 2 March, and browsers disagree about text like "2026-02-30". To check a date someone typed, build it from its parts and see whether the parts survived:

JavaScript
const bad = new Date("not a date");
console.log(String(bad), Number.isNaN(bad.getTime()));

function isRealDate(year, month, day) {          // month 1 to 12, as people write it
  const d = new Date(year, month - 1, day);
  return d.getFullYear() === year && d.getMonth() === month - 1 && d.getDate() === day;
}
console.log(isRealDate(2026, 2, 28), isRealDate(2026, 2, 30), isRealDate(2028, 2, 29));

Time zones and UTC

A Date is one moment, but people around the world see different local times for it. Methods without "UTC" in their name (and toLocale... without a timeZone option) use the visitor's time zone. To show a time in a specific zone, pass timeZone:

JavaScript
const launch = new Date(Date.UTC(2026, 9, 31, 12, 0));   // 12:00 UTC
const opts = { hour: "2-digit", minute: "2-digit", timeZoneName: "short" };
console.log(launch.toLocaleTimeString("en-IN", { ...opts, timeZone: "Asia/Kolkata" }));
console.log(launch.toLocaleTimeString("en-GB", { ...opts, timeZone: "Europe/London" }));
console.log(launch.toLocaleTimeString("en-US", { ...opts, timeZone: "America/New_York" }));

Store and send dates as UTC (toISOString() or a timestamp), and convert to local time only when you show them.

Relative times: "3 days ago"

Intl.RelativeTimeFormat writes phrases like "in 2 days" or "yesterday":

JavaScript
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
console.log(rtf.format(-1, "day"), "|", rtf.format(2, "day"), "|", rtf.format(-3, "hour"), "|", rtf.format(1, "week"));

function ago(date, now) {
  const seconds = Math.round((date - now) / 1000);
  const units = [["year", 31536000], ["month", 2592000], ["day", 86400], ["hour", 3600], ["minute", 60]];
  for (const [unit, size] of units) {
    if (Math.abs(seconds) >= size) return rtf.format(Math.round(seconds / size), unit);
  }
  return rtf.format(seconds, "second");
}
const now = new Date(2026, 9, 31, 12, 0);
console.log(ago(new Date(2026, 9, 31, 11, 55), now), "|", ago(new Date(2026, 9, 28), now));

Classic date programs

JavaScript
// age on a given day
function age(birth, on) {
  let years = on.getFullYear() - birth.getFullYear();
  const hadBirthday = on.getMonth() > birth.getMonth() ||
    (on.getMonth() === birth.getMonth() && on.getDate() >= birth.getDate());
  return hadBirthday ? years : years - 1;
}
console.log(age(new Date(2007, 7, 15), new Date(2026, 9, 31)));

// how many days in a month: day 0 of the NEXT month is the last day of this one
const daysInMonth = (year, month) => new Date(year, month + 1, 0).getDate();
console.log(daysInMonth(2026, 1), daysInMonth(2024, 1), daysInMonth(2026, 9));

// is it a weekend?
const isWeekend = (d) => d.getDay() === 0 || d.getDay() === 6;
console.log(isWeekend(new Date(2026, 9, 31)), isWeekend(new Date(2026, 10, 2)));

// days until an exam
const DAY = 86400000;
console.log(Math.ceil((new Date(2026, 11, 1) - new Date(2026, 9, 31)) / DAY), "days to go");

new Date(year, month + 1, 0) asks for "day 0" of the next month, which rolls back to the last day of this month: a neat trick for days-in-a-month.

Right now

These use the current moment, so your output is different every time:

JavaScript
const now = new Date();
console.log(now.toLocaleString("en-IN"));
console.log("Today is", now.toLocaleDateString("en-IN", { weekday: "long" }));
console.log("Milliseconds since 1970:", Date.now());

Key points

  • A Date is a moment: milliseconds since 1 Jan 1970 UTC; Date.now() is the current one.
  • Months run 0 to 11 and getDay() 0 (Sunday) to 6; days of the month start at 1.
  • Only ISO text (YYYY-MM-DD) parses reliably; a date-only string means midnight UTC.
  • Format with toLocaleDateString(locale, options) or Intl.DateTimeFormat; pass the locale for consistent results.
  • Subtract dates for milliseconds; divide by 86,400,000 for days; compare with getTime(), not ===.
  • setDate(getDate() + n) adds days and rolls over months and years.

Practice

  1. Print today's date as DD-MM-YYYY.
  2. Write daysUntil(dateText) that returns how many days remain until a date like "2026-12-25".
  3. Print the day of the week you were born on.
  4. Print every date in February 2028 that falls on a Sunday.
  5. Show the current time in Tokyo, London and New York.
Finished this lesson?Mark it complete to earn +20 XP and move your progress on.