JavaScript by Example

Dates

Creating, comparing, and formatting dates with the built-in Date object, and why you should store epoch milliseconds or ISO strings and format at the edge.

Date represents a single moment in time as the number of milliseconds since January 1, 1970 UTC (the Unix epoch). The object itself carries no timezone. The browser or runtime locale is only applied when you format the date as human-readable text.

You create a Date with new Date() for the current moment, new Date(milliseconds) for a specific instant, or new Date("YYYY-MM-DD") for an ISO date string. ISO 8601 strings are safe to parse across environments. Locale-formatted strings like "April 27, 2026" are not. .getTime() returns the raw epoch millisecond value.

const now = new Date();
console.log(now);              // e.g. 2026-04-27T12:34:56.789Z
 
const fromMs = new Date(0);
console.log(fromMs);           // 1970-01-01T00:00:00.000Z
 
const fromISO = new Date("2026-04-27");
console.log(fromISO.getTime()); // epoch milliseconds for that date
 
// Avoid locale-formatted strings - parsing varies by runtime
const risky = new Date("April 27, 2026"); // not reliable everywhere

< and > work between two Date objects because JavaScript coerces them to numbers (epoch ms) for the comparison. === does not work for equality because it compares object identity. Two separate Date objects representing the same moment are never === each other.

const a = new Date("2026-04-27");
const b = new Date("2026-04-27");
 
console.log(a === b); // false - different objects in memory
console.log(a < b);   // false
console.log(a > b);   // false
 
// Compare for equality using epoch milliseconds
console.log(a.getTime() === b.getTime()); // true
 
const later = new Date("2026-12-31");
console.log(a < later); // true
console.log(a > later); // false

Intl.DateTimeFormat formats a date for a specific locale and timezone - use it for what users read. .toISOString() produces a UTC string that is safe for storage, logs, and APIs. The two serve different purposes: one for humans, one for machines.

const date = new Date("2026-04-27T15:30:00Z");
 
// For users: locale-aware output
const formatter = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric",
  timeZone: "America/New_York",
});
console.log(formatter.format(date)); // "April 27, 2026"
 
// Short form
console.log(date.toLocaleDateString("en-US")); // "4/27/2026"
 
// For storage and APIs: UTC, always consistent
console.log(date.toISOString()); // "2026-04-27T15:30:00.000Z"

In production

Store epoch milliseconds or ISO strings in databases and APIs, never wall-clock strings like "April 27, 2026" - those are ambiguous outside the locale that produced them. Date is mutable, and === between two instances is always false, so treat dates as values you compare with .getTime(). For anything beyond rendering today's date (date arithmetic, relative formatting, timezone logic) reach for date-fns or Luxon, or wait for the Temporal API once your runtime ships it.

Enjoyed this? Get more essays on software craft delivered to your inbox.

Subscribe free