Why NOT to use Date
JavaScript's Date
object is one of the most problematic APIs in the language. While it seems convenient at first, it's the source of countless bugs and unpredictable behavior in applications. Here's why you should avoid it at any cost.
The Core Problems
1. No Timezone Support
The Date
object doesn't support explicit timezones. It only knows about the local system timezone and UTC, making it really difficult to work with dates in a predictable way.
const date = new Date("2024-01-01");
// Could be anything 0, 1, 23, etc. depending on browser or server timezone
console.log(date.getHours());
Machine-dependent behavior: The same code produces different results depending on where it's executed. This makes your application unpredictable and hard to test.
-
The JavaScript
Date
object reflects the host system's local timezone. -
There is no API in JavaScript to override this.
2. Confusing constructor
The Date
object's constructor string parser have strange behavours. These might lead to errors that are really hard to track down.
// Same same but different:
// Use a Date in the constructor, interpeted as UTC, no problem.
console.log(new Date("2024-01-01"));
// Add Time to the string, now it's interpreted as LOCAL time.
console.log(new Date("2024-01-01T00:00:00"));
// Add a Z to the end of the string, now it's interpreted as UTC again
console.log(new Date("2024-01-01T00:00:00Z"));
3. Confusing API Method Names
The Date
object has poorly named methods that don't match what you'd expect, making code hard to read and prone to errors.
// JavaScript Date has confusing method names and behaviors
const date = new Date("2024-01-15"); // Monday, January 15th, 2024
// Confusing: getDay() returns day of WEEK, not day of month
console.log(date.getDay()); // 1 (Monday)
// Expected: Should be called getDayOfWeek() or getWeekday()
// Confusing: 0 = Sunday, not Monday (differs from ISO 8601)
const sunday = new Date("2024-01-14");
console.log(sunday.getDay()); // 0 (Sunday)
// ISO 8601 standard: Monday = 1, Sunday = 7
// Confusing: getDate() returns day of MONTH, not the full date
console.log(date.getDate()); // 15 (day of month)
// Expected: Should be called getDayOfMonth()
// What you'd expect vs what you get:
// date.getDay() → should be day of month (15) → actually day of week (1)
// date.getDate() → should be full date → actually day of month (15)
// The correct but counter-intuitive way:
const dayOfWeek = date.getDay(); // 0-6, Sunday = 0
const dayOfMonth = date.getDate(); // 1-31
const fullDate = date.toString(); // Full date string
console.log(`Day of week: ${dayOfWeek}`); // 1
console.log(`Day of month: ${dayOfMonth}`); // 15
console.log(`Full date: ${fullDate}`); // Mon Jan 15 2024...
You would expect getDate()
to also use a 0-based index for the day of the month just like getDay()
, but it doesn't.
Counter-intuitive naming:
getDay()
returns day of week (0-6), not day of monthgetDate()
returns day of month (1-31), not the full date- Day of week uses 0=Sunday instead of ISO 8601 standard where Monday=1
4. Mutable State = Bugs
Date
objects are mutable, which might lead to unexpected side effects and hard-to-track bugs.
const date = new Date("2024-01-01"); // Creates a new Date 2024-01-01 00:00 UTC
console.log("Unmodified date", date);
const newDate = new Date(date.setDate(date.getDate() + 1));
console.log("New date", newDate);
console.log("Modified date", date);
Mutation bugs: Methods like setHours()
, setDate()
, etc. modify the original object instead of returning a new one.
- Violates the principle of immutability and can cause subtle bugs.
- Confusing return values:
setDate()
returns a timestamp, making it appear like an immutable function when it also mutates the original object.
5. Unpredictable Results
The same code returns different results depending on the system timezone, making unit testing unreliable.
// This represents midnight UTC on Jan 1, 2024
const date = new Date("2024-01-01");
const hours = date.getHours();
// Imagine this is unit tests, and you are testing a function that returns a date.
console.log(hours === 0); // ✅ Works in UTC
console.log(hours === 19); // ✅ Works in EST (UTC-5)
console.log(hours === 16); // ✅ Works in PST (UTC-8)
console.log(hours === 9); // ✅ Works in JST (UTC+9)
5. Not Serializable
Date
objects don't serialize well. When you send them over HTTP or store them in JSON, you lose the Date() object.
const date = new Date();
const json = JSON.stringify(date);
const parsed = JSON.parse(json);
// parsed.date is now a string, not a Date object
console.log(typeof date); // "object"
console.log(typeof parsed); // "string"
Serialization problems: Libraries like superjson
exist to work around this, but they add complexity and overhead to your application.
6. Only Supports Gregorian Calendar
The Date
object only works with the Gregorian calendar. While this calendar is widely used, many cultures use different calendar systems (like Islamic, Hebrew, Thai Buddhist, or Chinese calendars).
Limited calendar support: JavaScript Date() only supports the Gregorian calendar.
The Alternative: Timestamps
JavaScript has a much better built-in native type for handling dates: number
(timestamps)!
Why Timestamps Are Better
- Immutable: It's just numbers
- Serializable: Work perfectly with JSON
- Predictable: Always represent the same moment in time no matter environment, timezone or calendar system
- Efficient: No extra object allocation or garbage collection overhead
- Testable: Easy to create predictable tests that works everywhere.
// Clean, predictable, testable
const timestamp = Date.now();
const oneHourLater = timestamp + 60 * 60 * 1000;
// This works the same everywhere
console.log(oneHourLater - timestamp === 3600000);
Summary
The JavaScript Date
object is tricky and are a big cause of Date & Time related bugs in global applications.
Next, learn about why timestamps are the better choice for your applications.