The Date Object Is a Moment in Time
A Date in JavaScript represents a single instant — internally, just a number of milliseconds since January 1, 1970 UTC (the "Unix epoch"). Everything else — years, months, days, timezones, formatting — is a view layered on top of that number.
now.getTime() is the raw millisecond count. Everything a Date does — comparing, adding days, formatting — boils down to manipulating that number and then reinterpreting it.
Keep that model in your head. A Date isn't "March 14 in Paris." It's a universal instant that can be displayed as March 14 in Paris, or March 13 in Los Angeles, depending on the timezone you look at it through.
Creating Dates
There are four main ways to construct a Date:
Two things to notice:
- The parts constructor uses zero-indexed months.
2means March. January is0. This is a constant source of off-by-one bugs — everywhere else in the API, months are 0-indexed too, so at least it's consistent with itself. new Date("2026-03-14")(without a time) is parsed as UTC midnight.new Date("2026-03-14T09:30")(without aZ) is parsed as local time. This asymmetry is a classic gotcha.
For "right now" as a number, prefer Date.now() — it skips the object allocation:
Date.now() is the right tool for measuring elapsed time, timeouts, and anything where you don't need calendar arithmetic.
Reading Parts of a Date
Once you have a Date, you pull components out with getters. There are two flavors of each: local-time and UTC.
The local ones depend on whoever's machine is running the code. If you're storing or comparing dates across users and servers, pick UTC explicitly or you'll chase phantom bugs. Rule of thumb: use UTC getters for anything going into a database or a log; use local getters for anything you're about to show a human.
Don't use getYear(). It's a legacy method that returns year - 1900 and exists only for compatibility. Always reach for getFullYear().
Formatting for Humans
Avoid date.toString() for anything you care about — its output is locale- and engine-dependent. There are two formatters worth knowing.
For a standard machine-readable string, use toISOString():
That's the format to use when logging, storing in JSON, or sending over the network. It's always UTC, always unambiguous.
For a human-facing string, use Intl.DateTimeFormat or the toLocale* methods, which wrap it:
Intl.DateTimeFormat handles locales, timezones, and every combination of fields you'd want. Reach for it before writing manual ${year}-${month}-${day} formatting — that kind of string building is where off-by-one month bugs live.
Comparing Dates
Two Date objects that represent the same instant are not === equal — === checks object identity, not value. Compare their timestamps instead:
For ordering, the comparison operators work directly because they coerce to numbers:
Subtraction gives you the gap in milliseconds. Divide by 1000 * 60 * 60 * 24 for days. Write that constant out the first time; after a while you'll recognize 86_400_000 on sight.
Date Arithmetic
There's no addDays method. The idiomatic way is to use setDate, setMonth, etc. — they accept out-of-range values and roll over correctly:
Two things worth calling out:
new Date(date)copies the date.setDatemutates, so always copy first or you'll modify the caller's value.setDate(35)on a 31-day month rolls into the next month automatically. Same forsetMonth(14)— it advances the year. This makes arithmetic far less painful than it looks.
For anything complex — business days, recurring events, durations with month awareness — reach for a library (date-fns, Luxon, or the upcoming Temporal API). Rolling your own calendar math past "add a few days" is a tar pit.
Timezone Reality Check
Timezones are the single biggest source of date bugs. The rules worth internalizing:
- A
Datestores a UTC instant. The timezone is applied only when you read parts out or format it. - The timezone used by
getHours(),getDate(), etc. is the local timezone of the machine running the code. Servers and browsers often disagree. new Date("2026-03-14")(date only) parses as UTC.new Date("2026-03-14T00:00")(with time, no zone) parses as local.new Date(2026, 2, 14)(parts) is local.
When you need a specific timezone for display, pass timeZone to Intl.DateTimeFormat:
Same instant, two views. The Date object itself hasn't changed.
A Small Working Example
Putting it together — a function that formats how long ago something happened:
Timestamps in, human-readable string out. This is the shape of 90% of real-world date code: subtract two instants, divide by a unit, round, format.
What You Take Away
- A
Dateis a UTC instant. Timezones appear when you read or format it. - Use
Date.now()for timestamps,new Date()for calendar work. - Use
toISOString()for storage and logs,Intl.DateTimeFormatfor users. - Compare with
getTime()or</>. Never===. - Months are 0-indexed. Watch the date-only string parsing trap.
- For serious date math, use a library.
Next: URLs and Query Strings
Dates often show up in URLs — filter by date range, pass a timestamp as a query parameter. Parsing and building URLs by hand is just as bug-prone as formatting dates by hand, and the standard library has a URL object that handles it cleanly. That's next.
Frequently Asked Questions
How do you get the current date in JavaScript?
Call new Date() with no arguments. It returns a Date object representing the moment the constructor ran. If you only want a numeric timestamp (milliseconds since 1970), use Date.now() — it's faster and doesn't allocate a full object.
How do you compare two dates in JavaScript?
Compare their timestamps, not the Date objects themselves. a.getTime() < b.getTime() works, and so does a < b because < coerces dates to numbers. But a === b does not — === checks object identity, so two Date objects representing the same instant are never strictly equal.
How do you format a date in JavaScript?
For anything user-facing, use Intl.DateTimeFormat or date.toLocaleDateString() — they handle locales and timezones properly. For machine-readable output, date.toISOString() gives you a standard string like 2026-03-14T09:30:00.000Z. Avoid date.toString() for storage; the format is locale-dependent.
Why is my JavaScript date off by one day?
Usually a timezone issue. new Date('2026-03-14') parses as UTC midnight, but date.getDate() returns the day in the local timezone — which can be the day before. Use getUTCDate() for the UTC day, or construct dates with new Date(year, month, day) which uses local time from the start.