JavaScript Dates Are Weird

The Date object is confusing. Here are the gotchas and how to handle them.

The Weirdness

Months are 0-indexed

new Date(2025, 0, 1)  // January 1, 2025
new Date(2025, 11, 1) // December 1, 2025

Why?! Because reasons.

Parsing is inconsistent

new Date("2025-01-15")           // UTC midnight
new Date("2025/01/15")           // Local midnight
new Date("January 15, 2025")     // Local midnight

// Different results depending on your timezone!

Mutation

const date = new Date();
const nextWeek = date;
nextWeek.setDate(date.getDate() + 7);

// BOTH are now next week! Assignment doesn't copy.

Basic Operations

Create Dates

// Current time
const now = new Date();

// Specific date (months are 0-indexed!)
const date = new Date(2025, 9, 6); // October 6, 2025

// From ISO string
const date2 = new Date("2025-10-06T10:00:00Z");

// From timestamp
const date3 = new Date(1728208800000);

Get Parts

date.getFullYear()  // 2025
date.getMonth()     // 9 (October, remember 0-indexed)
date.getDate()      // 6
date.getDay()       // 1 (Monday, 0 is Sunday)
date.getHours()     // varies by timezone
date.getTime()      // timestamp in ms

Format (Modern Way)

const date = new Date("2025-10-06");

// Built-in formatting
date.toLocaleDateString("en-US")
// "10/6/2025"

date.toLocaleDateString("en-US", { 
  weekday: "long", 
  year: "numeric", 
  month: "long", 
  day: "numeric" 
})
// "Monday, October 6, 2025"

new Intl.DateTimeFormat("en-US", {
  dateStyle: "full",
  timeStyle: "short"
}).format(date)
// "Monday, October 6, 2025 at 10:00 AM"

Libraries

For complex date logic, use a library:

date-fns

import { format, addDays, differenceInDays } from "date-fns";

format(new Date(), "yyyy-MM-dd")
// "2025-10-06"

addDays(new Date(), 7)
// Date object for next week

differenceInDays(dateA, dateB)
// Number of days between dates

Day.js (Moment.js successor)

import dayjs from "dayjs";

dayjs().format("YYYY-MM-DD")
// "2025-10-06"

dayjs().add(7, "day").format()
// ISO string for next week

dayjs("2025-01-01").diff(dayjs(), "day")
// Days until January 1

Best Practices

1. **Store as ISO strings or timestamps** - Avoid ambiguity 2. **Convert at the edges** - Parse on input, format on display 3. **Be timezone-aware** - Use UTC for storage 4. **Use libraries for logic** - Don't reinvent date math 5. **Test with different timezones** - Set TZ env var in tests

Dates are hard. Don't feel bad for struggling with them.

Back to Journal