Dates and time
Why it matters
Dates are in almost every application: the time of a chat message, a delivery date, "posted 3 days ago", a
calendar, a countdown to the end of a sale, the user's age. JavaScript has the built-in Date object for this. It
has a few traps that break programs for beginners — and for experienced developers too. Let's go through them.
Creating a date
const now = new Date(); // the current moment
const newYear = new Date(2026, 0, 1); // January 1, 2026, 00:00 local time
const iso = new Date("2026-11-02"); // from a string in ISO format
const fromMs = new Date(0); // from a timestamp
console.log(newYear.getFullYear(), newYear.getMonth(), newYear.getDate());
Output:
2026 0 1
The main trap: months are counted from zero. January is 0, February is 1, December is 11. But days of
the month start from one, as usual. That's why you write getMonth() + 1 when displaying the month.
Timestamps
Inside, Date stores one number — how many milliseconds have passed since January 1, 1970, Greenwich time
(UTC). This number is called a timestamp:
console.log(new Date(0).toISOString());
console.log(new Date("2026-01-01T00:00:00Z").getTime());
Output:
1970-01-01T00:00:00.000Z
1767225600000
The current timestamp is Date.now(). It's used to measure time:
const start = Date.now();
for (let i = 0; i < 1e6; i++) {}
console.log("The loop took less than a second: " + (Date.now() - start < 1000));
Output:
The loop took less than a second: true
Date parts
| Get | What it returns |
|---|---|
getFullYear() |
the year, for example 2026 |
getMonth() |
the month 0–11 |
getDate() |
the day of the month 1–31 |
getDay() |
the day of the week 0–6, where 0 is Sunday |
getHours(), getMinutes(), getSeconds() |
the time |
getTime() |
the timestamp |
const d = new Date(2026, 10, 2, 14, 5); // November 2, 2026, 14:05
const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
console.log(days[d.getDay()]);
console.log(`${d.getHours()}:${String(d.getMinutes()).padStart(2, "0")}`);
Output:
Mon
14:05
For each get… there is a set…: setFullYear, setMonth, setDate, setHours…
A date corrects itself
If you give a "non-existent" day, Date carries the extra over to the next month. It's not an error but a
handy tool:
console.log(new Date(2026, 1, 30).getDate(), new Date(2026, 1, 30).getMonth()); // February 30?
const d = new Date(2026, 11, 30);
d.setDate(d.getDate() + 5); // plus 5 days
console.log(d.getFullYear(), d.getMonth(), d.getDate());
const lastDay = new Date(2024, 2, 0); // "zeroth" of March = the last day of February
console.log(lastDay.getDate());
Output:
2 2
2027 0 4
29
February 30 became March 2, December 30 + 5 days is January 4 of the next year, and day 0 gives the last day of
the previous month (February 2024 had 29 days).
The difference between dates
If you subtract one date from another, you get the difference in milliseconds. To convert it to days, divide by
the number of milliseconds in a day: 1000 * 60 * 60 * 24 = 86400000:
const start = new Date("2026-09-01");
const end = new Date("2026-12-31");
const days = Math.round((end - start) / 86400000);
console.log("Days until the end of the year: " + days);
Output:
Days until the end of the year: 121
Math.round is needed because of daylight saving time in some countries: then a day can have 23 or 25 hours.
ISO and time zones
The most reliable format for a date in a string is ISO 8601: 2026-11-02T14:05:00Z. The letter Z means
"UTC time". Servers and JSON use it:
const d = new Date(Date.UTC(2026, 10, 2, 9, 30));
console.log(d.toISOString());
console.log(JSON.stringify({ created: d }));
Output:
2026-11-02T09:30:00.000Z
{"created":"2026-11-02T09:30:00.000Z"}
The same moment looks different in different countries: 09:30 UTC is 14:30 in Dushanbe (UTC+5) and 09:30 in London in winter (UTC+0). So:
getHours(),getDate()and the others return the time in the computer's time zone;getUTCHours(),getUTCDate()… — in UTC;new Date("2026-11-02")(date only) is parsed as midnight UTC, whilenew Date(2026, 10, 2)is midnight local time.
The rule: store and send dates in UTC (an ISO string or a timestamp), and convert to local time only when showing them to the user.
Nice output for people
toLocaleDateString and toLocaleTimeString format a date by the rules of a language:
const d = new Date(Date.UTC(2026, 10, 2, 9, 30));
console.log(d.toLocaleDateString("en-US", { timeZone: "UTC" }));
console.log(d.toLocaleDateString("en-GB", { timeZone: "UTC" }));
console.log(d.toLocaleDateString("en-US", { day: "numeric", month: "long", year: "numeric", timeZone: "UTC" }));
Output:
11/2/2026
02/11/2026
November 2, 2026
Without timeZone the user's time zone is used — on websites that's usually exactly what you need.
How it works inside
Date is a wrapper around one timestamp number. All the get… methods compute the year, month and hours from this
number taking the time zone into account, and set… recalculate the timestamp. That's why two dates are compared
by their timestamps: a.getTime() === b.getTime(). An ordinary a === b compares objects and gives false
even for identical dates, but < and > work because they turn dates into numbers.
The Date object is mutable: setDate changes it in place. If a date came into a function from outside and
you need to shift it, make a copy first: const copy = new Date(date);.
For complex work with dates (time zones, "in 2 months", calendars) projects often use the date-fns or dayjs
libraries, and newer versions of JavaScript are getting the built-in Temporal.
Common mistakes
- Months from zero.
new Date(2026, 1, 1)is February 1. When displaying —getMonth() + 1. getDayinstead ofgetDate.getDayis the day of the week,getDateis the day of the month.- Age by dividing by 365. Leap years cause an error of a day or two. Compare years, months and days.
- Comparing with
===. ComparegetTime(). - Changed someone else's date.
setDatechanges the object — make a copy. - A string in an unclear format. Different browsers understand
new Date("02.11.2026")differently. Use ISO2026-11-02or numbers.
Cheat sheet
| What | How |
|---|---|
| Now | new Date(), timestamp — Date.now() |
| A specific date | new Date(2026, 10, 2) (months from zero!) |
| From ISO | new Date("2026-11-02T14:05:00Z") |
| Parts | getFullYear(), getMonth() + 1, getDate(), getDay() |
| Shift | d.setDate(d.getDate() + 7) |
| Difference in days | Math.round((b - a) / 86400000) |
| To ISO | d.toISOString() |
| For people | d.toLocaleDateString("en-US") |
| Two digits | String(n).padStart(2, "0") |