Date & Intl
Working with dates and times in JavaScript — the Date object, common pitfalls, and Intl for locale-aware formatting.
setMonth(getMonth() + 1) looks like "one month later", but Date doesn't know what to do with 31 February. It quietly rolls the extra days into the next month, so a bill due on 31 January becomes due on 3 March. February is simply skipped. The same thing happens with 31 March plus one month (31 April becomes 1 May), and with setFullYear on 29 February.
function addOneMonth(date) {
const d = new Date(date);
d.setMonth(d.getMonth() + 1);
return d;
}
const due = addOneMonth(new Date(2026, 0, 31)); // 31 January
console.log(due.toDateString());
// Tue Mar 03 2026Working with dates and formatting them for users
Creating a Date
// The current date and time:
const now = new Date();
console.log(now); // something like: Fri Jun 27 2026 14:30:00 GMT+0530
// A specific date from an ISO string (recommended format):
const launch = new Date("2026-01-15");
const meeting = new Date("2026-06-27T14:30:00");
// From individual parts (trap: months are 0-indexed!):
const d = new Date(2026, 5, 27); // June 27, 2026 — month 5, not 6!
// This is one of JS's most famous gotchas. Always use ISO strings when possible."2026-06-27") avoids this entirely.
Getting parts of a date
const d = new Date("2026-06-27T14:30:00");
d.getFullYear() // 2026
d.getMonth() // 5 ← June (0-indexed — add 1 to display)
d.getDate() // 27 (day of month, 1-31)
d.getDay() // 6 (day of week: 0=Sunday, 6=Saturday)
d.getHours() // 14
d.getMinutes() // 30
d.getSeconds() // 0
// The safe, unambiguous string formats:
d.toISOString() // "2026-06-27T09:00:00.000Z" — UTC, always use for storage
d.toLocaleDateString() // "6/27/2026" — locale-dependent, only for display
d.toDateString() // "Sat Jun 27 2026" — human-readable but not locale-awareDate arithmetic — using timestamps
The safest way to do date arithmetic is with timestamps — milliseconds since the Unix epoch (1 Jan 1970 UTC). Every Date can become a timestamp and back.
// Get timestamp:
const now = Date.now(); // milliseconds since epoch (no object needed)
const ts = new Date().getTime(); // same thing via a Date object
// Calculate future/past dates:
const ONE_DAY_MS = 24 * 60 * 60 * 1000; // 86,400,000
const ONE_WEEK_MS = 7 * ONE_DAY_MS;
const today = new Date();
const tomorrow = new Date(today.getTime() + ONE_DAY_MS);
const lastWeek = new Date(today.getTime() - ONE_WEEK_MS);
console.log(tomorrow.toDateString()); // tomorrow's date
console.log(lastWeek.toDateString()); // last week
// Difference between two dates:
const start = new Date("2026-01-01");
const end = new Date("2026-06-27");
const diffMs = end.getTime() - start.getTime();
const diffDays = Math.floor(diffMs / ONE_DAY_MS);
console.log(`${diffDays} days between them`); // 177 daysFixed-length steps, such as days and weeks, work with timestamps as shown above. Months don't have a fixed length, so decide what "one month later" should mean and code it. The usual rule, used by banks and billing systems, is to keep the same day number but clamp it to the last day of the target month. Day 0 of a month gives you the last day of the month before, which is the shortest way to find that limit:
function addOneMonth(date) {
const d = new Date(date);
const day = d.getDate();
d.setDate(1); // park on the 1st so the month change can't overflow
d.setMonth(d.getMonth() + 1);
const lastDay = new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate(); // day 0 = last day of previous month
d.setDate(Math.min(day, lastDay)); // clamp: 31 Jan -> 28 Feb
return d;
}
console.log(addOneMonth(new Date(2026, 0, 31)).toDateString());
console.log(addOneMonth(new Date(2026, 2, 15)).toDateString());
// Sat Feb 28 2026
// Wed Apr 15 2026Intl — let the browser format dates and numbers
Never write your own date formatter. The Intl API does it correctly
for every locale, timezone, and language automatically.
const date = new Date("2026-06-27T14:30:00");
// DateTimeFormat — pick your locale and style:
new Intl.DateTimeFormat("en-US").format(date) // "6/27/2026"
new Intl.DateTimeFormat("en-GB").format(date) // "27/06/2026"
new Intl.DateTimeFormat("en-IN").format(date) // "27/6/2026"
new Intl.DateTimeFormat("de-DE").format(date) // "27.6.2026"
new Intl.DateTimeFormat("ja-JP").format(date) // "2026/6/27"
// Control what's shown:
const fmt = new Intl.DateTimeFormat("en-IN", {
weekday: "long", // "Saturday"
year: "numeric",
month: "long", // "June"
day: "numeric",
hour: "2-digit",
minute: "2-digit",
timeZoneName: "short"
});
console.log(fmt.format(date));
// "Saturday, 27 June 2026 at 02:30 pm IST" (Node 22; punctuation varies by browser)
// NumberFormat for currency:
const price = 1234.56;
new Intl.NumberFormat("en-IN", { style: "currency", currency: "INR" }).format(price)
// "₹1,234.56"
new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(price)
// "$1,234.56"Relative time: "3 days ago"
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
rtf.format(-1, "day") // "yesterday"
rtf.format(-3, "day") // "3 days ago"
rtf.format(0, "day") // "today"
rtf.format(1, "day") // "tomorrow"
rtf.format(-2, "week") // "2 weeks ago"
rtf.format(1, "month") // "next month"
// Calculate which unit to use:
function timeAgo(date) {
const diffMs = date.getTime() - Date.now();
const diffSec = Math.round(diffMs / 1000);
const diffMin = Math.round(diffSec / 60);
const diffHr = Math.round(diffMin / 60);
const diffDay = Math.round(diffHr / 24);
if (Math.abs(diffSec) < 60) return rtf.format(diffSec, "second");
if (Math.abs(diffMin) < 60) return rtf.format(diffMin, "minute");
if (Math.abs(diffHr) < 24) return rtf.format(diffHr, "hour");
return rtf.format(diffDay, "day");
}
console.log(timeAgo(new Date(Date.now() - 5 * 60 * 1000))); // "5 minutes ago"Official Sources
- MDN Web Docs — Date
- MDN Web Docs — Intl
- TC39 Temporal Proposal
- ECMAScript Internationalisation API (ECMA-402)
- MDN Web Docs — Intl.RelativeTimeFormat
The Codex links only to official documentation and the ECMAScript specification. No third-party tutorials.