Skip to main content

JavaScript Dates

JavaScript Date Pitfalls

Written by Published

A few date behaviours catch out almost every JavaScript developer.

The Date API is one of the oldest parts of the language.

Knowing its four main traps prevents most date bugs.

Example

Example

javascript

const date = new Date(2026, 1, 30);

console.log(date.getMonth());
console.log(date.getDate());

February has no 30th, so it rolled over into March.

No error was raised at all.

The Four Traps

Months start at 0.

Out-of-range values roll over silently.

An invalid date gives NaN rather than throwing.

Dates are mutable, so setters change the original.

Syntax

Syntax

javascript

Number.isNaN(date.getTime())

This is how you check a date is valid.

Silent Rollover

This is occasionally useful but usually a surprise.

Example

Example

javascript

const date = new Date(Date.UTC(2026, 0, 32));

console.log(date.getUTCMonth());
console.log(date.getUTCDate());

January 32 became February 1.

Invalid Dates

An unparseable string gives an Invalid Date, not an error.

Every getter on it returns NaN.

Example

Example

javascript

const date = new Date("not a date");

console.log(Number.isNaN(date.getTime()));
console.log(String(date));

Always check before using a parsed date.

Checking Validity

A small helper is worth having.

Example

Example

javascript

function isValid(date) {
  return date instanceof Date && !Number.isNaN(date.getTime());
}

console.log(isValid(new Date("2026-01-15")));
console.log(isValid(new Date("nonsense")));

The output is true then false.

Date-Only Strings Are UTC

"2026-01-15" is parsed as UTC midnight.

In a negative timezone that reads as the previous day locally.

Example

Example

javascript

const date = new Date("2026-01-15");

console.log(date.getUTCDate());
console.log(date.toISOString().slice(0, 10));

Reading it with getDate() could give 14 in some timezones.

Mutability Bites

A shared date changed in one place changes everywhere.

Example

Example

javascript

function addWeek(date) {
  const copy = new Date(date.getTime());
  copy.setUTCDate(copy.getUTCDate() + 7);
  return copy;
}

const original = new Date(Date.UTC(2026, 0, 1));
const later = addWeek(original);

console.log(original.getUTCDate());
console.log(later.getUTCDate());

Copying inside the function protects the caller's date.

Complete Example

Complete Example

html

<!DOCTYPE html>
<html>
<head>
  <title>JavaScript Date Pitfalls</title>
</head>
<body>

  <h1>Date Pitfalls</h1>

  <p id="out"></p>

  <script>
    function isValid(date) {
      return date instanceof Date && !Number.isNaN(date.getTime());
    }

    const rolled = new Date(Date.UTC(2026, 0, 32));
    const broken = new Date("not a date");

    document.getElementById("out").innerHTML =
      "January 32 becomes month " + rolled.getUTCMonth() +
        " day " + rolled.getUTCDate() +
      "<br>Is the broken date valid: " + isValid(broken) +
      "<br>February 30 exists: " + (new Date(Date.UTC(2026, 1, 30)).getUTCMonth() === 1);
  </script>

</body>
</html>

Try It Yourself

Run the above example in the Try It Editor.

Try another impossible date:

Use month 12 and see it roll into the next year.

Important Points

  • Months start at 0.
  • Out-of-range values roll over silently.
  • An invalid date gives NaN, not an error.
  • Check validity with Number.isNaN(date.getTime()).
  • Dates are mutable, so copy before changing.

Conclusion

These four traps account for most JavaScript date bugs.

Validating a parsed date costs one line and saves a great deal.

For heavy date work, a modern library is worth the dependency.