You have reached the beginning of time!

The History of Date in JavaScript

From the Beginning to Temporal in Node.js 26

A 31-year journey from a 10-day hack to a standards-grade date/time API

BLOG (31).png

Contents

  • 1995 — Date is Born in a Rush
  • 1997 — ECMAScript 1 Standardizes the Mess
  • 2000s — Learning to Live With the Pain
  • 2011 — Moment.js Saves the Day
  • 2014–2018 — The Age of Alternatives
  • 2017 — The Temporal Proposal is Born
  • 2017–2021 — Design and Stage 1 to Stage 2
  • 2021–2025 — Stage 3: Implementations and Years of Polishing
  • 2022 — Even Date Libraries Became a Security Risk
  • March 2026 — Stage 4: Temporal Enters the Standard
  • May 2026 — Node.js 26: Temporal On by Default
  • How to Upgrade to Node.js 26 for Free
  • Date vs. Temporal: Before and After
  • What Happens to Moment.js and Friends?
  • References

1 — 1995: Date is Born in a Rush

When Brendan Eich created JavaScript in just 10 days in 1995 for Netscape Navigator, the Date implementation was borrowed directly from Java — and Java had in turn taken it from java.util.Date, which already had its own serious problems.

The result was an API that shipped to the world with factory-installed defects.

ProblemExample
Months are 0-indexed (0 to 11)new Date(2026, 0, 1) is January
Weekdays are 0-indexed (0 = Sunday)Guaranteed confusion
getYear() returns year minus 1900new Date().getYear() returns 126 in 2026
Objects are mutable by defaultAny function can silently alter your date
No real time-zone supportOnly UTC and the system's local zone
String parsing is non-deterministicnew Date("2/3/2026") — February 3 or March 2?
// The classic 0-indexed month bug
const christmas = new Date(2026, 11, 25); // 11 = December, not an error
console.log(christmas.getMonth()); // 11

// Silent mutability: a nightmare
const date = new Date();

function doSomething(d) {
 d.setFullYear(1900); // modifies the original!
}

doSomething(date);

console.log(date.getFullYear()); // 1900

2 — 1997: ECMAScript 1 Standardizes the Mess

With the first official ECMAScript specification (ES1), Date was formally standardized with all its quirks intact.

The foundational principle of “don’t break the web” that guides TC39 meant those bugs would become practically eternal. Any attempt to fix them retroactively would break billions of existing web pages.

The web was growing explosively and JavaScript was everywhere. There was neither the time nor the appetite for a breaking redesign.

The industry collectively decided to live with it.


3 — 2000s: Learning to Live With the Pain

The community developed a set of defensive patterns to work safely with dates:

// Comparing dates: only via .getTime()
if (date1.getTime() === date2.getTime()) {
 /* ... */
}

// Cloning to avoid silent mutation
const copy = new Date(original.getTime());

// The most popular "safe" parsing pattern
const parts = "2026-05-22".split("-");
const date = new Date(parts[0], parts[1] - 1, parts[2]); // -1 required !!

// "Safe" formatting by hand (no built-in formatter until Intl)
function formatDate(d) {
 return d.getFullYear() + "-"
   + String(d.getMonth() + 1).padStart(2, "0") + "-"  // +1 !!
   + String(d.getDate()).padStart(2, "0");
}

These workarounds were repeated millions of times across codebases worldwide.

Entire coding conventions emerged just to avoid the traps baked into Date.


4 — 2011: Moment.js Saves the Day

Moment.js became one of the most downloaded libraries in the JavaScript ecosystem.

Its fluent, human-readable API was a breath of fresh air:

// So comfortable it felt like cheating...
moment("2026-05-22").add(7, "days").format("DD/MM/YYYY"); // "29/05/2026"
moment("2026-05-22").fromNow(); // "3 hours ago"
moment.tz("2026-05-22", "America/New_York").utcOffset(); // -240

Moment.js proved beyond any doubt that a decent date API was possible in JavaScript.

It became a fixture in virtually every serious web project.

But it had its own Achilles heel: mutable objects, a massive bundle size (~67 KB minified), no tree-shaking support, and performance problems at scale.

It demonstrated what was possible, but at a cost.


5 — 2014–2018: The Age of Alternatives

The community began searching for lighter, more modern alternatives.

LibraryYearApproach
date-fns2015Pure functions, fully immutable, fully tree-shakeable
Luxon2017Built by the Moment.js team; leverages the native Intl API for time zones and formatting
Day.js2018Moment-compatible API in only ~2 KB
// date-fns: the functional style — pure, predictable, tree-shakeable
import { addDays, format } from "date-fns";

format(addDays(new Date(), 7), "dd/MM/yyyy");

// Luxon: built on Intl, real timezone support
import { DateTime } from "luxon";

DateTime.now().setZone("America/Chicago").toISO();

// Day.js: Moment's API, one-seventh of its size
dayjs("2026-05-22").add(7, "day").format("DD/MM/YYYY");

Each library solved real problems, but they all shared one fundamental limitation: they were wrappers or replacements built on top of — or entirely separate from — the native runtime.

Bundling date logic remained a cost that every JavaScript application had to pay.


6 — 2017: The Temporal Proposal is Born at TC39

Developers from Bloomberg, Igalia, and the broader community formally opened the Temporal proposal at TC39.

The motivation was unambiguous: replace Date with a correct-by-design API, not a patch.

The founding principles of Temporal were:

  • Complete immutability — all operations return new objects
  • Real time zones, not just offsets
  • Separate types for each use case (date only, time only, instant, zoned, etc.)
  • Deterministic parsing via ISO 8601 exclusively
  • Full non-Gregorian calendar support
  • Explicit and unambiguous API surface — no implicit coercions

“The goal of Temporal is not to be a replacement for Moment.js. It is to be the correct foundation that JavaScript should have had from the start.”

— TC39 Temporal Champions


7 — 2017–2021: Design and Stage 1 to Stage 2

For years, the proposal champions — Philipp Dunkel, Maggie Johnson-Pint, Matt Johnson-Pint, Shane F. Carr, and others — iterated over the design.

It was an enormous undertaking. Replacing Date required handling:

  • The world's calendar systems: Chinese lunisolar, Islamic, Hebrew, Japanese imperial era, Ethiopian, and more
  • The IANA TZDB (the global database of time zones), including DST transitions
  • Duration arithmetic with variable-length months and years
  • Deep integration with the existing Intl API (ECMA-402)
  • Edge cases in wall-clock time arithmetic at DST transitions

Timeline

  • 2017 — Stage 1 — Exploratory proposal accepted by TC39
  • 2019 — Stage 2 — Draft specification published
  • 2021 — Stage 3 — Specification complete; implementations begin

8 — 2021–2025: Stage 3, Implementations and Years of Polishing

In Stage 3, browsers and runtimes began experimental implementations.

Users could opt in behind flags:

// Available behind experimental flags in recent Node.js versions

// Node.js (v18+ with V8 experimental support):
node --harmony-temporal app.js

// Chrome:
chrome://flags  "Experimental JavaScript"

const today = Temporal.Now.plainDateISO();

console.log(today.toString()); // "2026-05-22"

These years exposed unanticipated edge cases: bugs in duration arithmetic across DST boundaries, unexpected behavior in exotic calendars, ambiguous wall-clock times during clock changes. Each one required revisions to the spec. The champions chose thoroughness over speed — a decision that paid off in the correctness of the final standard.

Browser support rolled out gradually during this period: Chrome and V8-based engines shipped incremental support starting in 2024, with broader availability through 2025.


9 — 2022: Even Date Libraries Became a Security Risk

By the early 2020s, third-party date libraries had become deeply embedded across the JavaScript ecosystem.

Applications relied on them for:

  • formatting
  • time zones
  • parsing
  • scheduling
  • localization
  • duration calculations

But this dependency came with growing operational and security concerns.

In 2022, Moment.js was affected by a widely discussed path traversal vulnerability (CVE-2022-31129), reminding developers that even foundational utility libraries can become part of an application's attack surface.

The issue was fixed, but it reinforced a broader realization across the ecosystem:

critical platform capabilities should ideally exist natively in the runtime itself — not exclusively through third-party dependencies.

That realization helped strengthen support for Temporal and the push toward a modern built-in date/time API for JavaScript.


10 — March 11, 2026: Stage 4, Temporal Enters the Standard

After 9 years of work, at the TC39 meeting of March 2026, Temporal officially reached Stage 4, making it part of ECMAScript 2026.

"Temporal is now Stage 4 at TC39. Thanks to all the other champions of JavaScript's new date-time API. It has been a wild ride."

— TC39 Temporal Champions, March 11, 2026

Alongside Stage 4, browser support solidified:

  • Chrome and Edge (V8) — enabled by default from January 2026 (v144+)
  • TypeScript 6.0 — full type definitions shipped in February 2026
  • Safari — partial support in Technology Preview
  • Firefox — implementation in progress

11 — May 5, 2026: Node.js 26, Temporal On by Default

Node.js 26, released on May 5, 2026 with V8 14.6 and Undici 8, enabled Temporal without any flags or experimental settings.

For the first time in JavaScript's history, developers have a first-class date/time API built directly into the runtime.

// No import. No npm install. No flags. Just modern JavaScript.

// Specialized types for every use case
const date      = Temporal.PlainDate.from("2026-05-22");
const time      = Temporal.PlainTime.from("14:30:00");
const dateTime  = Temporal.PlainDateTime.from("2026-05-22T14:30:00");
const instant   = Temporal.Now.instant();          
const zoned     = Temporal.Now.zonedDateTimeISO("America/Chicago");

// True immutability — methods return new objects
const tomorrow = date.add({ days: 1 });

console.log(date.toString());    
// "2026-05-22"  (unchanged)

console.log(tomorrow.toString());
// "2026-05-23"

// Arithmetic without off-by-one month bugs
const start = Temporal.PlainDate.from("2026-01-31");
const end   = start.add({ months: 1 });

console.log(end.toString());
// "2026-02-28"  (not "2026-03-03")

// Real time zones with automatic DST handling
const event = Temporal.ZonedDateTime.from(
 "2026-03-08T01:30:00[America/New_York]"
);

const oneHourLater = event.add({ hours: 1 });

// Correctly accounts for the clock jumping forward

// Unambiguous comparisons
const a = Temporal.PlainDate.from("2026-05-22");
const b = Temporal.PlainDate.from("2026-06-01");

console.log(Temporal.PlainDate.compare(a, b));
// -1 (a is before b)

// Expressive durations
const duration = Temporal.Duration.from({
 years: 1,
 months: 6,
 days: 3
});

console.log(duration.toString());
// "P1Y6M3D" (ISO 8601)

// 'until' and 'since': difference between dates
const today  = Temporal.Now.plainDateISO();
const target = Temporal.PlainDate.from("2026-12-31");
const diff   = today.until(target);

console.log(`${diff.days} days until end of year`);

12 — How to Upgrade to Node.js 26 for Free

If your infrastructure or production servers are still running an older version of Node.js and you want to take advantage of Temporal, built-in time zones, and all the other improvements in Node.js 26, you can upgrade at no cost.

Free Upgrade Program — NodeSource and OpenJS Foundation

NodeSource, in partnership with the OpenJS Foundation, has created a free upgrade program to help developers and organizations move to Node.js 26 safely and with support guidance.

Get started at:

https://nodesource.com/upgrade

The upgrade path from any currently supported or end-of-life version of Node.js is straightforward.

The program covers:

  • Step-by-step migration guidance for common Node.js versions
  • Compatibility checklists for breaking API removals in Node.js 26
  • Resources from the OpenJS Foundation for long-term support planning
  • Access to NodeSource's enterprise distribution channels

With Temporal now part of ECMAScript 2026 and active by default in Node.js 26, there has never been a better time to upgrade.

No third-party date libraries, no polyfills, no flags — just a modern runtime with a correct date/time API built in.


13 — Date vs. Temporal: Before and After

The old world

// Months start at 0 — always a trap
new Date(2026, 0, 1); // January (why?!)

// Silent mutability
const d = new Date();
d.setMonth(d.getMonth() + 1); // mutates the original

// Time zone ambiguity and implicit UTC conversion
const meeting = new Date("2026-05-22T10:00:00");

console.log(meeting.toString());
// Local machine timezone

console.log(meeting.toISOString());
// Converted to UTC automatically

// Non-deterministic string parsing
new Date("2/3/2026");

The new world

// Months start at 1 — exactly as expected
Temporal.PlainDate.from({
  year: 2026,
  month: 1,
  day: 1
});

// Complete immutability
const t = Temporal.Now.plainDateISO();
const next = t.add({ months: 1 });

// Explicit timezone-aware date/time
Temporal.ZonedDateTime.from(
  "2026-05-22T10:00:00[America/New_York]"
);

// Strict ISO 8601 parsing — always unambiguous
Temporal.PlainDate.from("2026-03-02");
FeatureDateTemporal
ImmutabilityNo — mutable by defaultYes — all operations return new objects
Month indexing0–111–12
Time zone supportUTC + system local onlyFull IANA TZDB, DST-aware
Calendar supportGregorian onlyMultiple calendars built in
String parsingNon-deterministic, locale-dependentISO 8601, strict and deterministic
Separate date/time typesNo — one object for everythingYes — PlainDate, PlainTime, ZonedDateTime, Instant, etc.
Duration typeNoYes — Temporal.Duration
Native in Node.js 26Since 1995Yes, enabled by default

14 — What Happens to Moment.js and Friends?

The Moment.js team had already declared the project in maintenance mode in 2020, recommending against using it in new projects.

With Temporal now in the ECMAScript standard and active in Node.js 26, the picture is clear:

LibraryStatus with Temporal
Moment.jsMaintenance mode since 2020 — migrate to Temporal for new projects
date-fnsGradually replaceable — Temporal covers the same functional style natively
Day.jsGradually replaceable — Temporal's API is more complete and correct
LuxonGradually replaceable — Temporal integrates deeply with Intl natively

For projects already using these libraries, migration is incremental.

Temporal does not remove Date — it coexists with it.

The standard provides interoperability methods:

// Convert between Date and Temporal
const legacyDate = new Date();

const instant = Temporal.Instant.fromEpochMilliseconds(
 legacyDate.getTime()
);

// Back to Date when needed (e.g., for legacy APIs)
const backToDate = new Date(instant.epochMilliseconds);
  • 1995 — Date is born (copied from Java, 10 days of work)
  • 1997 — ECMAScript 1 standardizes it — bugs included
  • 2011 — Moment.js saves developers from the suffering
  • 2015 — date-fns (functional immutability)
  • 2017 — Temporal proposed at TC39 (Stage 1)
  • 2017 — Day.js and Luxon emerge as modern alternatives
  • 2019 — Stage 2 (draft specification)
  • 2021 — Stage 3 (experimental implementations begin)
  • 2021 — Node.js ships it under --harmony-temporal flag
  • 2024 — Chrome/Edge enable it incrementally
  • 11 Mar 2026 — Stage 4 — part of ECMAScript 2026
  • 05 May 2026 — Node.js 26 — Temporal on by default

It took 31 years from the day Brendan Eich wrote Date in those frantic 10 days of 1995 for JavaScript to finally have the date/time handling it always deserved.

A reminder that in the world of web standards, good things take their time.


References

The NodeSource platform offers a high-definition view of the performance, security and behavior of Node.js applications and functions.

Start for Free