The definitive guide to JavaScript Dates

By

A complete guide to JavaScript dates: create, parse, validate, format, compare, calculate, handle time zones, avoid DST bugs, and start using Temporal.

~~~

Dates look simple until time zones, daylight saving time, and user input get involved.

This guide covers the built-in Date object from start to finish. We will create dates, parse and validate them, read and change their values, format them, compare them, and do date arithmetic safely.

At the end, we will also see the new Temporal API. Temporal gives JavaScript a much better model for dates and times.

Three social media posts from developers complaining about JavaScript date handling being difficult and frustrating

The most important thing to understand

A JavaScript Date represents one instant in time.

Internally, it stores a number. That number is the milliseconds elapsed since January 1, 1970 at 00:00:00 UTC.

const date = new Date('2026-08-10T09:30:00Z')

date.getTime() // 1786354200000

The Date does not store Europe/Rome, America/New_York, or any other named time zone.

Local methods interpret the instant using the computer’s time zone. UTC methods interpret the same instant as UTC.

This explains why the same Date can display a different hour on two computers.

Create a Date

There are 4 useful ways to create a Date.

Get the current date and time

Call new Date() without arguments to represent the current instant:

const now = new Date()

If you only need the current timestamp, use Date.now():

const timestamp = Date.now()

Date.now() returns a number and avoids creating a Date object.

Create a Date from a timestamp

Pass a timestamp in milliseconds:

const date = new Date(1786354200000)

Unix timestamps are often expressed in seconds instead. Multiply those values by 1000:

const unixTimestamp = 1786354200
const date = new Date(unixTimestamp * 1000)

The timestamp 0 represents the Unix epoch:

new Date(0).toISOString()
// '1970-01-01T00:00:00.000Z'

Create a Date from a string

Use the standard date-time format when exchanging dates between systems:

new Date('2026-08-10T09:30:00Z')
new Date('2026-08-10T11:30:00+02:00')

Both strings represent the same instant.

Z means UTC. +02:00 is an explicit offset from UTC.

Avoid ambiguous strings such as these:

new Date('08/10/2026')
new Date('August 10, 2026')

Non-standard parsing can behave differently across runtimes. Parse the fields yourself, use a library, or require a standard format.

There is also a surprising historical rule:

new Date('2026-08-10')
// midnight UTC

new Date('2026-08-10T00:00:00')
// midnight in the computer's local time zone

The first string is date-only and uses UTC. The second contains a time but no offset, so JavaScript uses local time.

My advice is to always include Z or an offset when the value represents an instant.

Create a Date from components

Pass the year, month, day, and optional time components:

const date = new Date(2026, 7, 10, 9, 30, 0)

This creates August 10, 2026 at 09:30 in the local time zone.

The month starts at zero:

  • 0 is January
  • 7 is August
  • 11 is December

The day starts at 1.

The complete order is:

new Date(year, month, day, hours, minutes, seconds, milliseconds)

You need at least a year and month for this form. Missing values default to the first day of the month at midnight.

Create a UTC timestamp from components

Date.UTC() accepts similar components, but interprets them as UTC:

const timestamp = Date.UTC(2026, 7, 10, 9, 30)
const date = new Date(timestamp)

Date.UTC() returns a timestamp, not a Date.

The year 0 to 99 trap

The component-based constructor treats years from 0 through 99 as 1900 through 1999:

new Date(50, 0, 1).getFullYear() // 1950

This old behavior also affects Date.UTC().

To create a year in that range, start with a known date and use setFullYear():

const date = new Date(0)
date.setUTCFullYear(50, 0, 1)
date.setUTCHours(0, 0, 0, 0)

Most applications never need ancient years. Still, this is good to know when building reusable date code.

Parse a date string

Date.parse() parses a string and returns a timestamp in milliseconds:

const timestamp = Date.parse('2026-08-10T09:30:00Z')

It follows the same parsing rules as new Date(string).

I usually prefer new Date(string) when I need a Date, and Date.parse() when I need a timestamp.

Do not use either method as a flexible parser for user input. 03/04/2026 can mean March 4 or April 3, depending on the reader.

Check if a Date is valid

JavaScript can create an invalid Date without throwing an error:

const date = new Date('not a date')

date.toString() // 'Invalid Date'

Check its timestamp with Number.isNaN():

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

Use it like this:

isValidDate(new Date()) // true
isValidDate(new Date('not a date')) // false

Be careful with impossible calendar dates. Some inputs overflow instead of becoming invalid:

new Date(2026, 1, 31)
// March 3, 2026 in local time

If the user enters separate year, month, and day fields, check that the resulting components still match the input.

Read date and time components

Every local getter has a UTC counterpart.

const date = new Date('2026-08-10T09:30:45.123Z')

Use these methods for the computer’s local time zone:

date.getFullYear()
date.getMonth() // 0 to 11
date.getDate() // 1 to 31
date.getDay() // 0 is Sunday
date.getHours()
date.getMinutes()
date.getSeconds()
date.getMilliseconds()

Use these methods for UTC:

date.getUTCFullYear()
date.getUTCMonth()
date.getUTCDate()
date.getUTCDay()
date.getUTCHours()
date.getUTCMinutes()
date.getUTCSeconds()
date.getUTCMilliseconds()

Notice the difference between getDate() and getDay().

getDate() returns the day of the month. getDay() returns the weekday, from 0 for Sunday to 6 for Saturday.

Get the time zone offset

getTimezoneOffset() returns the difference between local time and UTC, in minutes:

const offset = new Date().getTimezoneOffset()

The sign often feels backwards. In UTC+2 it returns -120.

The value can also change during the year because of daylight saving time. Do not treat the current offset as a permanent property of a location.

Change a Date

Date objects are mutable. Setter methods change the original object.

const date = new Date(2026, 7, 10, 9, 30)

date.setDate(11)

The main local setters are:

date.setFullYear(year)
date.setMonth(month)
date.setDate(day)
date.setHours(hours)
date.setMinutes(minutes)
date.setSeconds(seconds)
date.setMilliseconds(milliseconds)
date.setTime(timestamp)

Most have UTC equivalents:

date.setUTCFullYear(year)
date.setUTCMonth(month)
date.setUTCDate(day)
date.setUTCHours(hours)
date.setUTCMinutes(minutes)
date.setUTCSeconds(seconds)
date.setUTCMilliseconds(milliseconds)

Setters accept overflowing values. JavaScript carries the overflow into the next unit:

const date = new Date(2026, 7, 10)

date.setDate(32)
// September 1, 2026

This behavior is useful for arithmetic, but it can also hide invalid input.

Clone a Date before changing it

Assigning a Date to another variable does not copy it:

const original = new Date()
const copy = original

copy.setDate(copy.getDate() +