Creating Date Objects
// Current date and time
const now = new Date();
console.log(now); // current timestamp
// From a string (ISO 8601 recommended)
const d1 = new Date('2026-06-15'); // date only (midnight UTC)
const d2 = new Date('2026-06-15T10:30:00'); // local time
const d3 = new Date('2026-06-15T10:30:00Z'); // UTC explicitly
const d4 = new Date('2026-06-15T10:30:00+05:30'); // UTC+5:30 (IST)
// From timestamp (milliseconds since Unix epoch: Jan 1 1970 UTC)
const d5 = new Date(0); // 1970-01-01T00:00:00.000Z
const d6 = new Date(1000); // 1970-01-01T00:00:01.000Z
const d7 = new Date(Date.now()); // now (same as new Date())
// From year, month, day, ... (month is 0-indexed!)
const d8 = new Date(2026, 5, 15); // June 15, 2026 local time
// β 5 = June (0=Jan, 1=Feb, ..., 11=Dec)
const d9 = new Date(2026, 0, 1, 9, 30, 0); // Jan 1, 2026 09:30:00
// Invalid date
const invalid = new Date('not a date');
console.log(isNaN(invalid)); // true
console.log(invalid.toString()); // 'Invalid Date'
// Date.now() β fastest way to get current timestamp (no object creation)
const timestamp = Date.now(); // number of milliseconds
console.log(typeof timestamp); // 'number'
In JavaScript's Date constructor and getMonth(), months are zero-indexed: January = 0, February = 1, ..., December = 11. This is one of the most common sources of off-by-one bugs. Always remember to add 1 when displaying the month: date.getMonth() + 1.
Getting Date Parts
const d = new Date('2026-06-15T14:30:45.500');
// All getters return LOCAL time (unless using UTC variants)
console.log(d.getFullYear()); // 2026
console.log(d.getMonth()); // 5 β June (0-indexed!)
console.log(d.getMonth() + 1); // 6 β human-readable
console.log(d.getDate()); // 15 (day of month)
console.log(d.getDay()); // day of week: 0=Sun, 1=Mon, ..., 6=Sat
console.log(d.getHours()); // 14
console.log(d.getMinutes()); // 30
console.log(d.getSeconds()); // 45
console.log(d.getMilliseconds()); // 500
console.log(d.getTime()); // Unix timestamp in ms
// UTC variants (always in UTC regardless of local timezone)
console.log(d.getUTCHours()); // could differ from getHours()
console.log(d.getUTCMonth()); // also 0-indexed
// Compute age from birthdate
function getAge(birthDate) {
const today = new Date();
let age = today.getFullYear() - birthDate.getFullYear();
const m = today.getMonth() - birthDate.getMonth();
if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {
age--;
}
return age;
}
const age = getAge(new Date(1998, 2, 15)); // March 15, 1998
console.log(age); // 28 (in 2026)
Setting Date Parts
const d = new Date('2026-06-15');
d.setFullYear(2027); // change year
d.setMonth(11); // change to December (0-indexed)
d.setDate(31); // change day of month
d.setHours(9); // change hour
d.setMinutes(30);
d.setSeconds(0);
d.setMilliseconds(0);
console.log(d); // 2027-12-31T09:30:00.000...
// JavaScript auto-adjusts overflow dates
const overflow = new Date(2026, 1, 30); // Feb 30 doesn't exist
console.log(overflow.toISOString()); // Automatically becomes Mar 2, 2026
// Set to start/end of day
function startOfDay(date) {
const d = new Date(date);
d.setHours(0, 0, 0, 0); // hours, minutes, seconds, milliseconds
return d;
}
function endOfDay(date) {
const d = new Date(date);
d.setHours(23, 59, 59, 999);
return d;
}
Date Arithmetic
Dates are stored as milliseconds internally. Arithmetic is done by converting to milliseconds, calculating, and converting back.
const MS_PER_DAY = 1000 * 60 * 60 * 24;
// Days between two dates
function daysBetween(date1, date2) {
const ms = Math.abs(date2.getTime() - date1.getTime());
return Math.round(ms / MS_PER_DAY);
}
const start = new Date('2026-01-01');
const end = new Date('2026-06-15');
console.log(daysBetween(start, end)); // 165 days
// Add N days to a date
function addDays(date, days) {
const result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
const tomorrow = addDays(new Date(), 1);
const nextWeek = addDays(new Date(), 7);
// Add months (handles variable month lengths)
function addMonths(date, months) {
const result = new Date(date);
result.setMonth(result.getMonth() + months);
return result;
}
// Compare dates (convert to timestamps for comparison)
const d1 = new Date('2026-01-01');
const d2 = new Date('2026-12-31');
console.log(d1 < d2); // true
console.log(d1.getTime() === d2.getTime()); // false
// Is a date in the past?
const isPast = (date) => date.getTime() < Date.now();
Formatting Dates
const d = new Date('2026-06-15T14:30:00');
// toLocaleDateString / toLocaleTimeString / toLocaleString
console.log(d.toLocaleDateString('en-GB')); // '15/06/2026'
console.log(d.toLocaleDateString('en-US')); // '6/15/2026'
console.log(d.toLocaleDateString('ja-JP')); // '2026/6/15'
console.log(d.toLocaleTimeString('en-US', { hour12: true }));
// '2:30:00 PM'
console.log(d.toLocaleString('en-GB', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
}));
// 'Monday, 15 June 2026 at 14:30'
// Intl.DateTimeFormat β for repeated formatting (more efficient)
const formatter = new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'short',
day: '2-digit',
timeZone: 'America/New_York'
});
const dates = [new Date('2026-01-15'), new Date('2026-06-15')];
dates.forEach(date => console.log(formatter.format(date)));
// Jan 15, 2026
// Jun 15, 2026
// Relative time formatting
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
console.log(rtf.format(-1, 'day')); // 'yesterday'
console.log(rtf.format(3, 'day')); // 'in 3 days'
console.log(rtf.format(-2, 'week')); // '2 weeks ago'
15/06/2026 6/15/2026 2026/6/15 2:30:00 PM Monday, 15 June 2026 at 14:30 Jan 15, 2026 Jun 15, 2026 yesterday in 3 days 2 weeks ago
Timezone Considerations
// JavaScript Date always stores UTC internally
// Display methods (getHours, toLocaleDateString) use local timezone
const d = new Date('2026-06-15T00:00:00Z'); // midnight UTC
console.log(d.getHours()); // could be 23, 1, etc. based on local TZ
console.log(d.getUTCHours()); // always 0
// Get local timezone offset (minutes from UTC; negative means ahead of UTC)
const offsetMinutes = new Date().getTimezoneOffset();
console.log(offsetMinutes); // e.g., -60 for UTC+1 (BST), 330 for IST
// Format in a specific timezone with Intl
const tokyoTime = new Intl.DateTimeFormat('en-US', {
timeZone: 'Asia/Tokyo',
hour: '2-digit',
minute: '2-digit',
hour12: false
}).format(new Date());
console.log(`Tokyo: ${tokyoTime}`);
// Best practice: store/transmit dates as UTC ISO strings
const event = {
title: 'Conference',
startAt: new Date('2026-09-01T09:00:00Z').toISOString()
// '2026-09-01T09:00:00.000Z'
};
For recurring events, business days, calendar grids, or complex timezone handling, reach for date-fns or Temporal (the upcoming built-in replacement for the Date API). The date-fns library is tree-shakeable, immutable, and covers virtually every date use case.
| Method | Returns | Note |
|---|---|---|
getFullYear() | 4-digit year | Use this, not deprecated getYear() |
getMonth() | 0β11 | Add 1 for human display |
getDate() | 1β31 | Day of month |
getDay() | 0β6 | 0 = Sunday, 6 = Saturday |
getHours() | 0β23 | Local time |
getTime() | ms since epoch | Use for arithmetic and comparison |
Date.now() | ms since epoch | Static, no object needed |
toISOString() | ISO 8601 string | Always UTC |
Interview Questions
- Why does
getMonth()return 0 for January? - How do you calculate the number of days between two dates?
- What is the difference between
getHours()andgetUTCHours()? - How do you format a date for display in a specific locale?
- Why is
Date.now()preferred overnew Date().getTime()?
ποΈ Practical Exercise
Write a countdown(targetDate) function that returns an object { days, hours, minutes, seconds } representing the time remaining until the target date. If the target date has passed, return all zeros. Test it with a date 7 days in the future.
π₯ Challenge Exercise
Build a Calendar class that, given a year and month, generates a 2D array representing the month's grid (rows = weeks, cols = days SunβSat). Each cell should contain either null (empty) or a date object for that day. Ensure the first row correctly aligns to the right day of the week. Display the grid as a formatted string.
Frequently Asked Questions
- Why is working with dates in JavaScript so error-prone?
- Several quirks combine: months are 0-indexed (Jan = 0), parsing behaviour varies by browser, local timezone vs UTC confusion is easy to make, and DST transitions can cause dates to shift. Libraries like
date-fnsor the upcomingTemporalAPI address these issues. - What is the Temporal API?
Temporalis the upcoming TC39 proposal to replace theDateobject. It has immutable date/time objects, proper timezone support, nanosecond precision, and no 0-indexed months. As of 2026, it is at Stage 3 and available behind flags in some environments.- How do I check if two Date objects represent the same day (ignoring time)?
- Compare the year, month, and date parts:
a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate(). Alternatively, compare their ISO date strings:a.toISOString().slice(0, 10) === b.toISOString().slice(0, 10). - Why does
new Date('2026-06-15')parse as midnight UTC whilenew Date('June 15 2026')parses as local time? - ISO 8601 date-only strings are treated as UTC by the specification. Non-ISO strings are implementation-dependent and many engines treat them as local time. Always use ISO format or explicit timestamps to avoid ambiguity.
- What is
Date.now()useful for? - Any situation where you need a timestamp without creating a
Dateobject: performance timing (const start = Date.now()), generating unique IDs, recording event times, or measuring elapsed time. It is slightly faster thannew Date().getTime().
π Summary
- Create dates with
new Date(), ISO strings, timestamps, or year/month/day arguments. getMonth()is 0-indexed β January is 0, December is 11. Always add 1 for display.- Use
Date.now()for the current timestamp without creating a Date object. - Date arithmetic is done in milliseconds: convert to
.getTime(), calculate, convert back. - Use
toLocaleDateString(locale, options)orIntl.DateTimeFormatfor locale-aware formatting. - Use
Intl.RelativeTimeFormatfor "3 days ago" / "in 2 hours" style formatting. - Always store and transmit dates as UTC ISO strings to avoid timezone confusion.
- For complex date logic, use
date-fnsor wait for theTemporalAPI.