Dates and Timestamps in JavaScript: A Developer's Guide
June 10, 2026 · 6 min read
Dates and Timestamps in JavaScript: A Developer's Guide
Dates in JavaScript are notoriously painful. Between timezone gotchas, the 0-indexed month, string parsing inconsistencies, and the verbosity of Intl.DateTimeFormat, it's easy to introduce subtle bugs that only appear in production — or worse, in a specific timezone you don't test in. This guide covers everything you need to work with dates reliably.
For quick conversions, try the Unix Timestamp Converter, Date Difference Calculator, and Time Zone Converter.
Unix Timestamps
A Unix timestamp is the number of seconds elapsed since January 1, 1970 00:00:00 UTC (the Unix epoch). It's timezone-agnostic, which makes it the safest way to store and transfer point-in-time values.
Seconds vs Milliseconds
JavaScript's Date works in milliseconds, not seconds. Most other systems (Unix, databases, APIs) use seconds. This mismatch is a constant source of bugs.
// Current time
Date.now() // milliseconds since epoch → e.g. 1749542400000
Date.now() / 1000 // seconds → e.g. 1749542400
// Convert a Unix timestamp (seconds) to a Date
const unixSeconds = 1749542400;
const date = new Date(unixSeconds * 1000); // must multiply by 1000
// Convert a Date back to a Unix timestamp
const timestamp = Math.floor(date.getTime() / 1000); // getTime() returns ms
A useful sanity check: a Unix timestamp in seconds is currently 10 digits. If you see 13 digits, it's milliseconds.
Date.now() vs new Date()
Date.now() // number (milliseconds) — fast, no object allocation
new Date() // Date object — needed when you want to call methods on it
new Date().getTime() // equivalent to Date.now()
+new Date() // unary plus coerces to number — also works
new Date() Pitfalls
Month Indexing Is 0-Based
January is 0, December is 11. This is one of JavaScript's most famous design mistakes.
new Date(2026, 0, 1) // January 1, 2026
new Date(2026, 11, 31) // December 31, 2026
// Easy way to remember: getMonth() returns 0-11, so setMonth() uses the same
const d = new Date();
d.getMonth() // 0 for January
Always add 1 when displaying the month to users: d.getMonth() + 1.
String Parsing Is a Minefield
This is the biggest source of timezone bugs in JavaScript:
// ISO 8601 date-only string → parsed as UTC midnight
new Date("2026-01-15")
// → Wed Jan 14 2026 19:00:00 GMT-0500 (Eastern Standard Time)
// The date is CORRECT in UTC but the local time is the previous day
// Slash-separated string → parsed as LOCAL midnight
new Date("2026/01/15")
// → Thu Jan 15 2026 00:00:00 GMT-0500 (Eastern Standard Time)
Rule: YYYY-MM-DD (ISO 8601 date-only) is treated as UTC. YYYY/MM/DD is treated as local. The difference matters if you're displaying a date without a time component and care which calendar day the user sees.
To safely parse a date as local midnight, use the date constructor with explicit parts:
const [year, month, day] = "2026-01-15".split("-").map(Number);
const localDate = new Date(year, month - 1, day); // local midnight, no timezone shift
Full ISO 8601 Strings Are UTC
When a string includes a time component and Z or an offset, parsing is unambiguous:
new Date("2026-01-15T00:00:00Z") // UTC midnight — always unambiguous
new Date("2026-01-15T00:00:00+05:30") // IST midnight — unambiguous
new Date("2026-01-15T12:30:00") // NO offset → browser treats as LOCAL
Best practice: always include Z or a timezone offset when passing ISO strings to new Date().
Formatting Dates
toISOString(): Machine-Readable Format
new Date().toISOString()
// "2026-01-15T17:30:00.000Z"
This always outputs UTC in ISO 8601 format. Use it for API payloads, logging, and anywhere machines will parse the value.
Intl.DateTimeFormat: Human-Readable Format
Intl.DateTimeFormat is the standard modern approach to locale-aware date formatting:
const date = new Date("2026-01-15T12:00:00Z");
// Basic usage
new Intl.DateTimeFormat("en-US").format(date)
// "1/15/2026"
new Intl.DateTimeFormat("en-GB").format(date)
// "15/01/2026"
// With options
new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric",
}).format(date);
// "January 15, 2026"
// With time
new Intl.DateTimeFormat("en-US", {
dateStyle: "medium",
timeStyle: "short",
}).format(date);
// "Jan 15, 2026, 12:00 PM"
Reuse a formatter instance if you're formatting many dates — creating a new Intl.DateTimeFormat per call has overhead:
const fmt = new Intl.DateTimeFormat("en-US", { dateStyle: "short" });
dates.map(d => fmt.format(d));
toLocaleDateString / toLocaleTimeString
These are convenience wrappers around Intl.DateTimeFormat:
date.toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" });
// "January 15, 2026"
date.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit" });
// "12:00 PM"
IANA Timezones
Every Intl.DateTimeFormat option accepts a timeZone parameter using IANA timezone identifiers:
const utcDate = new Date("2026-01-15T00:00:00Z");
new Intl.DateTimeFormat("en-US", {
timeZone: "America/New_York",
dateStyle: "full",
timeStyle: "long",
}).format(utcDate);
// "Wednesday, January 14, 2026 at 7:00:00 PM EST"
new Intl.DateTimeFormat("en-US", {
timeZone: "Asia/Tokyo",
dateStyle: "full",
timeStyle: "long",
}).format(utcDate);
// "Thursday, January 15, 2026 at 9:00:00 AM JST"
Common IANA identifiers: "America/New_York", "America/Los_Angeles", "Europe/London", "Europe/Paris", "Asia/Tokyo", "Asia/Kolkata", "Australia/Sydney", "UTC".
Get the user's local timezone:
Intl.DateTimeFormat().resolvedOptions().timeZone
// "America/Chicago"
Date Arithmetic
JavaScript has no built-in "add N days" method. The standard approach is manual millisecond math:
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
const today = new Date();
const tomorrow = new Date(today.getTime() + ONE_DAY_MS);
const lastWeek = new Date(today.getTime() - 7 * ONE_DAY_MS);
Gotcha: this approach breaks around Daylight Saving Time transitions. If you add exactly 24 hours to a date that crosses a DST boundary, you'll land at the wrong clock time. To reliably add calendar days:
function addDays(date, days) {
const result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
addDays(new Date("2026-03-08"), 1); // correctly handles spring-forward DST
setDate() handles month rollovers too — setDate(32) on a January date rolls over to February.
Calculating Date Differences
const start = new Date("2026-01-01");
const end = new Date("2026-12-31");
const diffMs = end - start; // JavaScript coerces to timestamps
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
// 364
For a quick UI, use the Date Difference Calculator.
Using date-fns for Reliability
For production code that does heavy date arithmetic, date-fns is the recommended library. It's tree-shakeable, immutable (doesn't mutate your dates), and handles DST correctly:
import { addDays, differenceInDays, format, parseISO } from "date-fns";
import { formatInTimeZone } from "date-fns-tz";
addDays(new Date(), 30);
differenceInDays(new Date("2026-12-31"), new Date("2026-01-01")); // 364
format(new Date(), "MMM d, yyyy"); // "Jun 10, 2026"
// Timezone-aware formatting
formatInTimeZone(new Date(), "Asia/Tokyo", "yyyy-MM-dd HH:mm zzz");
Common Mistakes Summary
| Mistake | Fix |
|---|---|
new Date("2026-01-15") shifts date in some timezones |
Use new Date(2026, 0, 15) for local dates |
Passing Unix seconds to new Date() |
Multiply by 1000 first |
| 0-indexed months | Always getMonth() + 1 for display |
new Date("2026/01/15") on non-Chrome browsers |
Use ISO format or explicit constructor |
Hardcoding +5:30 instead of IANA timezone |
Use "Asia/Kolkata" — DST-aware |
| Adding milliseconds for calendar days | Use setDate() instead |
ISO 8601 Quick Reference
ISO 8601 is the international standard for date/time strings. JavaScript's toISOString() always outputs it.
Date: 2026-01-15
Time (UTC): 2026-01-15T12:30:00Z
Time with offset: 2026-01-15T17:30:00+05:30
Week: 2026-W03
Ordinal: 2026-015 (day 15 of the year)
Prefer ISO 8601 for all stored and transmitted date values. Use the Unix Timestamp Converter when working with epoch values, the Date Difference Calculator for spans between two dates, and the Time Zone Converter for converting a time between IANA zones.