Edited by humans. Written by AI. How our editing works
All articles

JavaScript Date Handling: From Broken Basics to Temporal

A deep dive into JavaScript's notoriously broken Date object, the underrated Intl API, and why TC39's Temporal proposal took nearly a decade to arrive.

Dev Kapoor

Written by AI. Dev Kapoor

July 2, 20267 min read
Share:
Man with glasses smiling next to JS logo and analog clock on blue background

Photo: AI. Quinn Adler

There's a particular kind of developer despair that only manifests at 2 a.m. when a calendar app is showing Tokyo users yesterday's meetings. You didn't write bad code. You wrote code that worked fine in your local timezone and promptly fell apart the moment someone on a different continent touched it. Tapas Adhikary, a software engineer with more than two decades of production debugging behind him, opens his recent freeCodeCamp course on JavaScript dates and times with exactly this scenario — and uses it to make a point that most tutorials skip entirely.

"Dates and times are one of those topics that looks so simple on the surface," Adhikary says. "But once you deploy your application on production and somebody — your user — accesses that application from another side of the globe, they start seeing something off. Maybe by a day, maybe by a few hours, maybe things are broken on February 29th."

The course runs over two hours and covers a lot of ground. But its real argument isn't about API methods. It's about a mental model — and why most JavaScript developers are missing one.

The problem is epistemological before it's technical

Time, as Adhikary frames it, is fundamentally relative. There's no universal "now." The earth is round, the sun hits different places at different times, and humans have divided the planet into timezone offsets to make civil life legible. While recording his video, Adhikary notes, it was 3:00 p.m. in New York, 8:00 p.m. in London, 9:00 p.m. in Berlin, and 4:00 a.m. the following day in Tokyo — all the same moment.

This is the context programmers have to work inside. And it compounds quickly: timezones don't offset by whole hours only — India and Bangladesh differ by 30 minutes; some zones differ by 15 or 45. Governments can and do change timezone rules with little notice. Daylight Saving Time is observed inconsistently across countries. Months vary in length and leap year logic has three conditions, not one.

The universal reference point that tames this complexity is Unix epoch time: January 1, 1970, at exactly 00:00:00 UTC. From that moment, every passing millisecond gets a unique number. Adhikary describes it as a "practical starting point Unix engineers settled on" — and that framing is apt. Per research documented by Baeldung, the date wasn't chosen for profound reasons; it was simply a round, recent number that suited the Unix development era. Every timestamp in JavaScript is a count of milliseconds from that origin, stored internally as a single number regardless of where in the world the code runs.

The practical upshot: always store dates in UTC, always render them in the user's local timezone. This is the core rule the entire course circles back to, and it's the one rule that, when violated, causes most of the production bugs Adhikary describes.

The gotcha gallery — and the one that never stops biting people

JavaScript's Date object is famously full of sharp edges. Adhikary walks through the main ones: four different ways to construct a Date, local vs. UTC getter methods, the mutation trap in setters, month indexing that starts at zero.

That last one — zero-indexed months — is worth pausing on because it's probably the single most-searched Date-related bug on Stack Overflow, consistently surfacing in threads with titles like "why does getMonth() return 11 for December." It's not obscure. Developers who've shipped JavaScript professionally for years still occasionally get burned by it, usually in a date-picker component or an age-calculation utility. The fix is simple — add 1 before displaying, or maintain a months array that maps index to name — but the cause is a design decision that made sense in 1995 when consistency with other zero-indexed structures felt more important than human-readable output.

The mutation gotcha is subtler and arguably more dangerous. Setter methods on the Date object mutate the original in place. If you write an addDays function that calls date.setDate(date.getDate() + n) on the date passed in, you've modified the caller's reference — not returned a new date. The bug usually doesn't surface immediately. It surfaces when some other part of the application that held a reference to "original" discovers it's now seven days in the future.

Adhikary's solution is to always clone the date object before mutating: new Date(originalDate.getTime()). "Avoid mutation," he says plainly in the course. "Always clone your date object." It's the kind of advice that appears obvious written down and still gets missed under deadline pressure.

Intl.DateTimeFormat: the API developers keep not using

Part two of the course makes a case for Intl.DateTimeFormat that I think deserves more traction than it gets in the JavaScript community. According to MDN Web Docs, Intl.DateTimeFormat is now supported across all major modern browsers and Node.js — and yet the default response when someone needs locale-aware date formatting is still often "install moment.js" or "install date-fns."

Adhikary's explanation for why: "Many developers don't use it because they are not very much aware of it, and they end up using some external libraries that go out of date. They have to manage, migrate, and all these things."

That's a community-knowledge problem more than a technical one. The API is genuinely capable. Pass a locale tag like en-US, de-DE, or ja-JP, and the runtime consults Unicode's Common Locale Data Repository (CLDR) — the world's largest database of locale-aware formatting rules — to produce correct output. You don't maintain lookup tables. You don't handle right-to-left languages manually. You don't hardcode month names in twelve languages. You pass an options object and let the platform do the work.

The options system is granular enough to handle most real-world display cases: dateStyle: "full" gives you "Sunday, March 15, 2026"; dateStyle: "short" gives you "3/15/26." You can specify weekday, hour12, timeZone, and format individual date components independently. One formatter, any locale.

The performance implication is worth noting: constructing Intl.DateTimeFormat objects is relatively expensive, so Adhikary recommends creating formatters once and reusing them — particularly in contexts where you're formatting many dates in a loop.

Temporal: a decade of TC39 process politics, landing soon

The Date object is old. It was written in ten days in 1995, modeled closely on Java's java.util.Date — a class that Java itself later deprecated because of how broken it was. JavaScript kept the same object, same quirks, for three decades.

The Temporal proposal is what TC39 — the standards committee that governs JavaScript — has been building to replace it. This didn't happen quickly. The proposal has been in active development since around 2017, championed by community members including Maggie Johnson-Pint and later a dedicated champion group including Philip Chimento and others working within TC39's formal stages process. Getting a proposal through TC39 requires not just a good idea but consensus across browser vendors, runtime maintainers, and the broader implementer community — each of whom has different constraints and timelines. That political and organizational friction is part of why a clearly-needed fix took as long as it did.

What Temporal actually solves: the Date object conflates several distinct concepts that should be separate types. Temporal untangles them. Temporal.PlainDate represents a calendar date with no timezone. Temporal.ZonedDateTime represents a moment in time with a specific timezone — something the old Date object could never express cleanly. Temporal.Instant is a precise point in time, independent of any calendar. Each type does one thing; none of them silently defaults to local time and surprises you.

Browser support for Temporal is still arriving. Polyfills exist and are usable in production, but Adhikary's coverage of the side-by-side comparison with Date makes clear that the ergonomic improvement is substantial — not just syntactic sugar but a fundamentally different, less ambiguous approach to representing time in code.

The old Date will stick around for compatibility reasons, the same way bad decisions always do when they're baked into enough running code. But Temporal represents the first time TC39 has seriously addressed date-time handling as a design problem rather than an implementation detail. How long it takes to actually propagate into mainstream JavaScript codebases — through browser adoption, through framework updates, through the slow churn of existing Date usage being refactored — is a different question, and probably a more interesting one.

For now, understanding Date's failure modes, reaching for Intl.DateTimeFormat before npm-installing anything, and keeping Temporal's type system in mind as you design new date-handling utilities: that's about as solid a foundation as you can build while the landscape is still shifting.


— Dev Kapoor covers open source software and developer communities for Buzzrag.

From the BuzzRAG Team

We Watch Tech YouTube So You Don't Have To

Get the week's best tech insights, summarized and delivered to your inbox. No fluff, no spam.

Weekly digestNo spamUnsubscribe anytime

More Like This

RAG·vector embedding

2026-07-02
2,008 tokens1536-dimmodel text-embedding-3-small

This article is indexed as a 1536-dimensional vector for semantic retrieval. Crawlers that parse structured data can use the embedded payload below.