๐Ÿ“…Developer

The Right Way to Handle Dates and Times in JavaScript

Dates in JavaScript are notoriously painful. Here's what actually works for common date tasks without losing your mind to timezone bugs.

6 min readFebruary 3, 2026By FreeToolKit TeamFree to read

JavaScript's Date object is one of the original 1995 design decisions the language still carries. Months starting at 0, inconsistent string parsing, limited timezone support. The good news: modern approaches have made date handling manageable, and the Temporal API will eventually replace Date entirely.

The Timezone Rule

Store dates in UTC. Display in local time. This single rule prevents 80% of timezone bugs. When you store '2026-03-15T14:00:00Z' (the Z means UTC), there's no ambiguity. When you display it, convert to the user's local timezone. Never store 'March 15, 2026 at 2 PM' without a timezone โ€” that's an accident waiting to happen.

For Simple Use Cases: date-fns

date-fns is a collection of pure functions for date manipulation. Tree-shakeable, so you only import what you use. Immutable โ€” functions return new Date objects rather than mutating the input. The API is predictable: format(date, 'yyyy-MM-dd'), addDays(date, 7), isAfter(date1, date2). For most applications without complex timezone requirements, date-fns covers everything you need.

For Timezone-Aware Work: Luxon or the Temporal Polyfill

Luxon was created by the Moment.js team as the modern replacement. It handles IANA timezones correctly, supports locale-aware formatting, and has an immutable API. For scheduling applications, multi-timezone event systems, or any feature where 'what time is it in Tokyo right now' needs an accurate answer, Luxon or the Temporal polyfill are the right tools.

The native Date Parsing Problem

new Date('2026-03-15') gives you midnight UTC, which converts to March 14 in timezones behind UTC. new Date('2026/03/15') gives you midnight local time. Different string formats behave differently. The safe approach: always parse ISO 8601 strings (2026-03-15T00:00:00Z) explicitly or use a library. Never rely on new Date(someString) without knowing the format.

Formatting Dates Without Libraries

Intl.DateTimeFormat is built into modern browsers and does locale-aware date formatting without any library. new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric' }).format(date) gives 'March 15, 2026'. Change 'en-US' to 'de-DE' and you get '15. Mรคrz 2026'. This handles internationalization without shipping any additional JavaScript.

Debugging tip

When debugging a timezone-related bug, log date.toISOString() instead of date.toString(). The ISO string is always UTC and unambiguous. The toString() output varies by system locale and makes bugs harder to reproduce across environments.

Frequently Asked Questions

Why is JavaScript date handling so bad?+
The Date object was written in 10 days in 1995, inspired by Java's java.util.Date (which Java itself deprecated). Months are zero-indexed (January = 0, December = 11). The Date constructor has inconsistent behavior across browsers with string inputs. Timezone handling is limited. Daylight saving time adjustments cause subtle bugs. The Temporal API (currently Stage 3) is designed to fix all of this with a proper date/time library built into the language.
Should I still use Moment.js?+
No, unless you're maintaining an existing project that already uses it. Moment.js is 67KB minified, marked as legacy by its own maintainers, and has a mutable API design that causes subtle bugs. Modern alternatives: date-fns (tree-shakeable, functional), Day.js (Moment-compatible API, 2KB), or native Date with modern helpers if you don't need much. The Moment team explicitly recommends migrating.
How do I avoid timezone bugs?+
Store all dates as UTC in your database. Work with UTC on the server. Convert to local time only at the point of display. This one discipline eliminates the majority of timezone bugs. If you need to work with timezone-aware dates (events scheduled in a specific timezone regardless of viewer's location), store the IANA timezone name alongside the UTC time and use it explicitly during display.
What is the Temporal API?+
Temporal is the proposed replacement for JavaScript's Date object, currently in Stage 3 of the TC39 proposal process. It provides separate types for different concepts: PlainDate (just a date), PlainTime (just a time), ZonedDateTime (date, time, and timezone). It handles timezone arithmetic correctly, has an immutable API, and fixes virtually every Date API design flaw. It's available today through the @js-temporal/polyfill package.

๐Ÿ”ง Free Tools Used in This Guide

FT

FreeToolKit Team

FreeToolKit Team

We build free browser-based tools and write practical guides that skip the fluff.

Tags:

javascriptdeveloperdatesprogramming