JS Dates & Legacy Date Management
Master temporal representation in JavaScript: the Unix Epoch, the 4 Date constructor signatures,
zero-indexed month traps, UTC vs. Local offsets, modern Intl.DateTimeFormat internationalization,
and
the infamous mutability quirks that paved the way for the Modern Temporal API.
Table of Contents
15.1 The Architecture of Time: The Unix Epoch
In JavaScript, time is fundamentally represented as a single 64-bit floating point integer representing the number of milliseconds that have elapsed since midnight on January 1, 1970, UTC (Universal Coordinated Time). This benchmark moment is globally known as the Unix Epoch.
Internal Representation
The internal ECMAScript slot [[DateValue]] stores an integer millisecond counter. JavaScript
guarantees accurate date representation across a span of ±100,000,000 days
(roughly
273,790 years before and after 1970).
Date.now() Performance
To get the current timestamp, avoid calling new Date().getTime(). The static method
Date.now() returns the numeric epoch millisecond timestamp directly without allocating an
in-memory object wrapper.
Code Example: Accessing the Epoch Timestamp
// High performance timestamp generation (zero memory allocation)
const startTimestamp = Date.now();
console.log(startTimestamp); // e.g. 1790620980123
// Object instance timestamp
const now = new Date();
console.log(now.getTime()); // Exact same millisecond counter
console.log(Number(now)); // Implicit conversion invokes valueOf() returning ms
Try it Yourself »
15.2 The 4 Date Constructors & The Month Index Trap
The Date constructor has four distinct invocation signatures. Depending on the
arguments supplied, the JavaScript runtime parses your input as an instant, a string, or component pieces:
| Constructor Signature | Parameters | Description & Behavior |
|---|---|---|
new Date() |
None | Creates a Date object representing the exact moment of instantiation. |
new Date(milliseconds) |
integer |
Creates a date computed by adding the integer milliseconds to Jan 1, 1970 00:00:00 UTC. |
new Date(dateString) |
string |
Parses a date string (ISO 8601 recommended: "YYYY-MM-DDTHH:mm:ss.sssZ"). |
new Date(y, m, d, h, min, s, ms) |
2 to 7 integers |
Constructs a date using local calendar components.
Warning: Month index is 0-based! |
The Infamous Zero-Indexed Month Trap
In JavaScript:
• Months are indexed from 0 (January) to 11 (December).
• Days of the month are indexed from 1 to 31.
• Days of the week are indexed from 0 (Sunday) to 6 (Saturday).
Writing new Date(2026, 12, 1) does NOT create December 1, 2026! It overflows
to January 1, 2027.
Code Example: The Four Constructor Variants
// 1. Current moment
const current = new Date();
// 2. From millisecond offset
const epoch = new Date(0);
console.log(epoch.toUTCString()); // "Thu, 01 Jan 1970 00:00:00 GMT"
// 3. From ISO 8601 string (always use 'Z' for UTC or explicit timezone offsets)
const launch = new Date("2026-09-30T14:30:00Z");
// 4. Component constructor: (year, monthIndex, day, hours, minutes, seconds)
// Note: September is month index 8 (9 - 1)
const workshop = new Date(2026, 8, 30, 14, 30, 0);
console.log(workshop.toDateString()); // Wed Sep 30 2026
Try it Yourself »
Auto-Correction & Rollover Mechanics
When out-of-range component numbers are passed, JavaScript automatically rolls them over. For example:
new Date(2026, 0, 32) becomes February 1, 2026. Passing day 0
(new Date(2026, 2, 0)) rolls back to the last day of the previous month
(February 28/29). This is commonly leveraged as a handy shortcut to find days in any given month!
15.3 Getters & Setters: Local vs. UTC Time
Every Date instance provides two parallel sets of getter and setter methods:
one based on the user's local operating system timezone, and the other based on
Universal Coordinated Time (UTC).
| Time Field | Local Getter | UTC Getter | Return Range |
|---|---|---|---|
| 4-Digit Year | getFullYear() | getUTCFullYear() | e.g. 2026 |
| Month Index | getMonth() | getUTCMonth() | 0 - 11 |
| Day of Month | getDate() | getUTCDate() | 1 - 31 |
| Day of Week | getDay() | getUTCDay() | 0 (Sun) - 6 (Sat) |
| Hours | getHours() | getUTCHours() | 0 - 23 |
| Minutes | getMinutes() | getUTCMinutes() | 0 - 59 |
| Seconds | getSeconds() | getUTCSeconds() | 0 - 59 |
| Timezone Offset | getTimezoneOffset() | N/A (Always 0) | Difference from UTC in minutes |
The Timezone Offset Sign Inversion
date.getTimezoneOffset() returns the number of minutes local time is behind UTC.
• For New York (UTC-5): returns +300.
• For India (UTC+5:30): returns -330.
Keep this negative sign flip in mind whenever calculating offsets manually!
Code Example: Getters, Setters & Mutation
const meeting = new Date("2026-10-15T10:00:00Z");
// Reading both time frames
console.log("UTC Hour:", meeting.getUTCHours()); // 10
console.log("Local Hour:", meeting.getHours()); // e.g. 15 (if local is UTC+5:30)
// Mutating with Setters
meeting.setDate(meeting.getDate() + 7); // Move forward by 1 week
meeting.setHours(14, 30, 0); // Reschedule local time to 2:30 PM
console.log("Updated meeting:", meeting.toISOString());
Try it Yourself »
15.4 Date Formatting & Modern Intl.DateTimeFormat
Formatting dates manually using string concatenation is error-prone and ignores international linguistic
standards.
JavaScript provides built-in serializing methods and the powerful Intl.DateTimeFormat API:
Standard String Serializers
d.toISOString()→2026-09-29T15:30:00.000Zd.toDateString()→Tue Sep 29 2026d.toTimeString()→21:00:00 GMT+0530 ...d.toUTCString()→Tue, 29 Sep 2026 15:30:00 GMTd.toLocaleDateString()→9/29/2026(Local OS format)
The Modern Intl.DateTimeFormat Engine
The ECMA-402 Internationalization API allows full control over language, calendar systems, timezones, and formatting styles without external libraries like Moment.js.
High Performance & NativeCode Example: Internationalizing Dates with Intl.DateTimeFormat
const eventDate = new Date("2026-11-26T18:45:00Z");
// Format for Tokyo in Japanese
const tokyoFormatter = new Intl.DateTimeFormat("ja-JP", {
timeZone: "Asia/Tokyo",
dateStyle: "full",
timeStyle: "short"
});
console.log(tokyoFormatter.format(eventDate));
// "2026年11月27日金曜日 3:45"
// Format for London in British English
const londonFormatter = new Intl.DateTimeFormat("en-GB", {
timeZone: "Europe/London",
weekday: "long",
year: "numeric",
month: "long",
day: "numeric",
hour: "2-digit",
minute: "2-digit"
});
console.log(londonFormatter.format(eventDate));
// "Thursday, 26 November 2026 at 18:45"
Try it Yourself »
15.5 Date Arithmetic & Elapsed Time Calculations
Because JavaScript converts Date objects to numeric milliseconds in mathematical expressions,
calculating the difference between two timestamps is as simple as subtraction:
Code Example: Date Arithmetic & Elapsed Time in Milliseconds
const releaseDate = new Date("2026-12-31T00:00:00Z");
const currentDate = new Date("2026-09-30T00:00:00Z");
// Subtracting yields millisecond difference
const diffMs = releaseDate - currentDate;
// Time Unit Conversion Constants
const MS_PER_SECOND = 1000;
const MS_PER_MINUTE = 60 * MS_PER_SECOND;
const MS_PER_HOUR = 60 * MS_PER_MINUTE;
const MS_PER_DAY = 24 * MS_PER_HOUR;
const daysRemaining = Math.floor(diffMs / MS_PER_DAY);
console.log(`Days until launch: ${daysRemaining} days`); // 92 days
// Checking for Leap Years
function isLeapYear(year) {
return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
}
console.log("Is 2026 a leap year?", isLeapYear(2026)); // false
console.log("Is 2028 a leap year?", isLeapYear(2028)); // true
Try it Yourself »
15.6 Legacy Gotchas, Flaws & The Temporal Bridge
The JavaScript Date API was originally written by Brendan Eich in 1995 in under 10
days,
largely porting Java's flawed java.util.Date implementation. As a result, it suffers from several
architectural flaws:
1. Dangerous Mutability
Date instances are mutable. Calling date.setDate() modifies the original object
in place.
If a function receives a date object as a parameter and modifies it, caller functions experience
accidental
mutations and state corruption.
2. Inconsistent String Parsing
Date.parse("2026-05-01") (hyphenated ISO) is parsed as UTC. But
Date.parse("2026/05/01") (slashes) is parsed as Local Time! This timezone
drift causes off-by-one day bugs depending on client geographical location.
3. No Timezone Object Support
A Date object can only represent UTC and the system's local timezone. You cannot perform date
math
directly in a foreign timezone (e.g. adding 1 hour to Tokyo time during Daylight Saving transitions)
without
third-party libraries.
4. The Solution: Temporal API
TC39 developed the Modern JS Temporal API (covered in Module 4: Chapters 19 & 20).
Temporal fixes all of these issues with immutable data structures, wall-clock separation, and
first-class IANA timezone support.
15.7 Interactive Practice Labs
Lab 1: Global World Clock & Epoch Monitor
Live Ticking EngineObserve how the exact same internal millisecond timestamp translates to different local calendar times across global cities:
Lab 2: Date Difference & Time Delta Calculator
Lab 3: Intl.DateTimeFormat Studio
15.8 Hands-on Challenge & Chapter Quiz
Objective: Write a function addBusinessDays(startDate, numDays) that takes a
start
date and adds numDays to it, skipping weekends (Saturday & Sunday), and returns the new Date.
Clean Solution:
function addBusinessDays(startDate, numDays) {
// Clone to avoid mutating caller date!
const result = new Date(startDate);
let added = 0;
while (added < numDays) {
result.setDate(result.getDate() + 1);
const dayOfWeek = result.getDay(); // 0 is Sunday, 6 is Saturday
if (dayOfWeek !== 0 && dayOfWeek !== 6) {
added++;
}
}
return result;
}
// Verification:
const friday = new Date("2026-10-09T09:00:00Z"); // Friday
const delivery = addBusinessDays(friday, 3); // Should land on Wednesday (skipping Sat & Sun)
console.log(delivery.toDateString()); // "Wed Oct 14 2026"
Chapter 15 — Key Takeaways
Dateinternally stores milliseconds since Unix Epoch (Jan 1, 1970 UTC).Date.now()is the fastest way to get the current timestamp (no object allocation).- Month indices are 0-based: January = 0, December = 11.
- Local getters (e.g.
getHours()) vs UTC getters (e.g.getUTCHours()) can differ by your timezone offset.
getTimezoneOffset()returns the inverted offset in minutes (UTC+5:30 → -330).- Use
Intl.DateTimeFormatfor locale-aware, timezone-aware formatting without libraries. Dateobjects are mutable — always clone before passing to functions.- The Temporal API (Chapters 19–20) solves all legacy Date pitfalls with immutability and IANA timezone support.