To format a date in JavaScript, use Intl.DateTimeFormat or toLocaleDateString with a locale and options, for example date.toLocaleDateString("en-US", { dateStyle: "long" }) for March 5, 2026. Use toISOString() for APIs, the timeZone option to show another zone, and Intl.RelativeTimeFormat for phrases such as 3 days ago.
| Need | Use |
|---|---|
| Store or send a date | toISOString() |
| Show a date to people | Intl.DateTimeFormat / toLocaleDateString |
| Another time zone | the timeZone option |
| "3 days ago" | Intl.RelativeTimeFormat |
Updated
Pick a locale and options in the builder and copy the code it writes; the preview is formatted by your own browser. Below it, a table of everyday formats and runnable examples for zones, relative time and ranges.
Working with epoch seconds? Use the Unix timestamp converter.
| Option | Values |
|---|---|
| dateStyle / timeStyle | full, long, medium, short (cannot be mixed with the single-part options) |
| weekday | long, short, narrow |
| year | numeric, 2-digit |
| month | numeric, 2-digit, long, short, narrow |
| day, hour, minute, second | numeric, 2-digit |
| hourCycle | h11, h12, h23, h24 |
| timeZone | An IANA name such as Europe/Paris, or UTC |
| timeZoneName | short, long, shortOffset, longOffset |
| You want | Code | Gives (en-US, 5 March 2026, 14:07) |
|---|---|---|
| ISO 8601 for APIs | d.toISOString() | The same moment in UTC, ending in Z |
| YYYY-MM-DD in local time | d.toLocaleDateString("en-CA") | 2026-03-05 |
| MM/DD/YYYY | d.toLocaleDateString("en-US", { month: "2-digit", day: "2-digit", year: "numeric" }) | 03/05/2026 |
| DD/MM/YYYY | d.toLocaleDateString("en-GB") | 05/03/2026 |
| Long date | d.toLocaleDateString("en-US", { dateStyle: "long" }) | March 5, 2026 |
| Weekday and date | { weekday: "long", month: "long", day: "numeric" } | Thursday, March 5 |
| Time, 12-hour | d.toLocaleTimeString("en-US", { timeStyle: "short" }) | 2:07 PM |
| Time, 24-hour | { hour: "2-digit", minute: "2-digit", hourCycle: "h23" } | 14:07 |
| Relative | new Intl.RelativeTimeFormat("en", { numeric: "auto" }).format(-1, "day") | yesterday |
A date-only ISO string is read as UTC midnight, but a date-time string without an offset is read as local time. That difference causes most off-by-one-day bugs.
<script>
const a = new Date("2026-03-05"); // UTC midnight
const b = new Date("2026-03-05T00:00"); // local midnight
const c = new Date(2026, 2, 5); // local; months start at 0!
console.log(a.toISOString());
console.log(b.toISOString());
console.log(c.toDateString());
console.log("Your offset from UTC in minutes:", new Date().getTimezoneOffset());
</script>Build it from the local parts with padStart. The en-CA locale trick gives the same result but relies on that locale's format staying the same.
<script>
function ymd(d) {
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, "0");
const day = String(d.getDate()).padStart(2, "0");
return `${y}-${m}-${day}`;
}
console.log(ymd(new Date()));
console.log(new Date().toLocaleDateString("en-CA"));
</script>timeZone takes an IANA name. The Date itself has no zone; only the formatting changes. Note that timeZoneName cannot be combined with dateStyle or timeStyle.
<script>
const meeting = new Date("2026-03-05T15:00:00Z");
for (const tz of ["America/New_York", "Europe/London", "Asia/Karachi", "Australia/Sydney"]) {
const t = new Intl.DateTimeFormat("en-GB", { timeZone: tz, day: "numeric", month: "short", hour: "2-digit", minute: "2-digit", timeZoneName: "short" }).format(meeting);
console.log(tz.padEnd(18), t);
}
</script>Intl.RelativeTimeFormat writes the words; you pick the unit. This helper chooses the largest unit that fits.
<script>
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
const UNITS = [["year", 31536e6], ["month", 2592e6], ["week", 6048e5], ["day", 864e5], ["hour", 36e5], ["minute", 6e4], ["second", 1e3]];
function ago(date, now = Date.now()) {
const diff = date - now;
for (const [unit, ms] of UNITS) {
if (Math.abs(diff) >= ms || unit === "second") return rtf.format(Math.round(diff / ms), unit);
}
}
console.log(ago(Date.now() - 3 * 864e5));
console.log(ago(Date.now() + 2 * 36e5));
console.log(ago(Date.now() - 864e5));
</script>formatRange removes repeated parts, so a booking from the 5th to the 9th of the same month reads naturally.
<script>
const f = new Intl.DateTimeFormat("en-US", { month: "short", day: "numeric", year: "numeric" });
console.log(f.formatRange(new Date(2026, 2, 5), new Date(2026, 2, 9)));
console.log(f.formatRange(new Date(2026, 2, 28), new Date(2026, 3, 2)));
</script>When no preset fits, take the localised pieces and arrange them yourself, for example to style the day bigger than the month.
<script>
const parts = new Intl.DateTimeFormat("en-US", { weekday: "short", day: "numeric", month: "short" })
.formatToParts(new Date(2026, 2, 5));
console.log(parts);
const get = (t) => parts.find((p) => p.type === t).value;
console.log(`${get("weekday")} ${get("day")} ${get("month")}`);
</script>
Opening hours, event dates and blog dates should match how your visitors write them. A UK audience reads 05/03 as the 5th of March, a US one as May 3rd, so spell the month out (5 March 2026) whenever the audience is mixed.
For machine-readable dates, such as in an HTML time element or structured data, use the ISO form in the datetime attribute and the friendly form as the visible text.
Use the local year, month plus one and day, padded with padStart(2, "0"), and join them with hyphens. toISOString().slice(0, 10) also gives that shape but in UTC, so it can show the wrong day near midnight.
new Date("2026-03-05") is read as midnight UTC. West of Greenwich, that moment is still the 4th locally. Parse with a time ("2026-03-05T00:00") or with new Date(2026, 2, 5) to get local midnight.
Not for formatting. Intl covers locales, time zones and relative time in every modern browser. Moment's own maintainers describe it as a legacy project; for date arithmetic, a small library such as date-fns or the newer Temporal API (where available) works well.
Pass timeZone with an IANA name, for example { timeZone: "America/New_York", timeStyle: "short" }. The Date object stays the same instant; only how it is written changes.
Related: Unix timestamp converter, Date calculator, Military time converter, Promises, async/await and fetch, All free tools.
Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.
We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.