Introduction: Why Time Still Trips Up Developers
JavaScript's built-in Date object has long been a source of frustration. Its mutable nature, zero-based months, and inconsistent browser parsing can silently corrupt your data. Even experienced engineers at companies like Bloomberg have dedicated years to understanding these pitfalls—and they've helped create the upcoming Temporal proposal to finally fix them. This guide walks you through the most common date/time mistakes and shows you how to write robust code today, while preparing for the future with Temporal.

What You Need
- Basic JavaScript knowledge – Familiarity with functions, objects, and NPM packages
- Node.js (version 14+) or a modern browser (Chrome, Firefox, Edge) for testing
- A package manager (npm or yarn) to install optional libraries
- An editor like VS Code or WebStorm
- Optional but recommended: The Temporal polyfill for current experimentation
Step-by-Step Guide
Step 1: Identify the Classic Date Pitfalls
Before writing any date code, understand the three most common traps:
- Mutable operations: Methods like
setMonth()modify the original object. This leads to unintended side effects when passing dates around. - Zero-based months: January is
0, December is11. A typo likenew Date(2023, 12, 1)actually gives you January 1, 2024. - Browser-dependent parsing:
new Date('2023-01-02')is interpreted as midnight UTC in some browsers, but as local time in others. This ambiguity can shift dates by an entire day.
Step 2: Stop Using the Date Constructor with Strings
The single-argument string constructor is a major source of bugs. Instead, always parse dates explicitly using numeric arguments or ISO strings with timezone information. For example:
// BAD: ambiguous parsing
new Date('2023-04-01'); // could be UTC or local
// GOOD: explicit UTC
new Date(Date.UTC(2023, 3, 1)); // April is month 3
// GOOD: use a library like date-fns
const { parseISO } = require('date-fns');
parseISO('2023-04-01T00:00:00Z');
Step 3: Prefer UTC Methods for Portability
When working with dates across time zones, use the getUTC* and setUTC* methods. This ensures consistent behavior regardless of the user's local time. For example, store timestamps as UTC and only convert to local time for display.
Step 4: Adopt a Robust Date Library
Until Temporal reaches Stage 4, use a battle-tested library to avoid reinventing the wheel. Popular choices include:
- Luxon – Immutable, built-in time zones, modern API
- date-fns – Function-based, tree-shakeable
- Day.js – Lightweight, Moment-like API
These libraries handle parsing, formatting, and arithmetic correctly across environments.

Step 5: Prepare for the Temporal Proposal
The Temporal proposal introduces immutable types like Temporal.PlainDate, Temporal.PlainTime, Temporal.ZonedDateTime, and Temporal.Duration. To get started now:
- Install the polyfill:
npm install @js-temporal/polyfill - Import and play:
const { Temporal } = require('@js-temporal/polyfill'); - Create a plain date:
Temporal.PlainDate.from('2023-04-01') - Add durations:
plainDate.add({ days: 5 })(returns a new object)
Step 6: Migrate Existing Code Gradually
Don't rewrite everything overnight. Start with new features using Temporal or your chosen library. Then refactor high-risk date logic (user input parsing, time zone conversions) step by step. Unit tests are essential: create a test suite that verifies your date operations across different time zones and daylight saving transitions.
Tips
- Always specify a time zone in your application's configuration. Store all dates as UTC in the database and convert only for display.
- Avoid string date arithmetic – adding days by manipulating string parts is error-prone. Use library methods or Temporal's
.add(). - Test around DST boundaries – Never assume that a day has 24 hours. Use a library that respects the IANA time zone database.
- Consider using ISO 8601 for all date/time interchange. It's machine-readable, human-readable, and TZ-aware.
- Monitor the Temporal proposal's progress – Once it reaches Stage 4 and is adopted by Node and browsers, you can remove your library dependency and use native Temporal.
By following these steps, you'll minimize date/time bugs in your JavaScript applications today and be ready for the cleaner, safer future that Temporal promises. Remember: time may be a social construct, but your software doesn't have to break because of it.