You can format a date with a timezone in TypeScript by passing an IANA zone name, like America/New_York, as the timeZone option of Intl.DateTimeFormat or toLocaleString(). The Date itself never changes; only the text you print does:
new Intl.DateTimeFormat("en-US", { timeZone: "America/Chicago", timeStyle: "short" }).format(date);
I tested the code below with TypeScript 7.0.2 and Node.js v22.22.2 in strict mode. My test machine’s clock was pinned to US Central time, which matters more than you’d think.
We’ll cover the built-in formatter for each US zone, the machine time zone trap, an ISO string with an offset, daylight saving time, validating zone names and the type errors you’ll hit along the way.
Format a date in a specific time zone with Intl.DateTimeFormat
A JavaScript Date holds a single moment as milliseconds since 1970 UTC. It has no zone of its own. The zone only appears when you turn it into text, so that’s where you pass it:
const launch = new Date("2026-10-16T15:00:00Z"); // one instant in time
const zones: string[] = [
"America/New_York",
"America/Chicago",
"America/Denver",
"America/Phoenix",
"America/Los_Angeles",
];
for (const timeZone of zones) {
const text = new Intl.DateTimeFormat("en-US", {
timeZone,
weekday: "short",
month: "short",
day: "numeric",
year: "numeric",
hour: "numeric",
minute: "2-digit",
timeZoneName: "short",
}).format(launch);
console.log(timeZone.padEnd(20), text);
}
// dateStyle/timeStyle are a shortcut, but they can't be combined with timeZoneName
const styled = new Intl.DateTimeFormat("en-US", {
dateStyle: "full",
timeStyle: "short",
timeZone: "America/Los_Angeles",
});
console.log("dateStyle + timeStyle:", styled.format(launch));
try {
new Intl.DateTimeFormat("en-US", { dateStyle: "medium", timeZoneName: "short" });
} catch (err) {
console.log("dateStyle + timeZoneName ->", (err as Error).name);
}
Output:
America/New_York Fri, Oct 16, 2026, 11:00 AM EDT
America/Chicago Fri, Oct 16, 2026, 10:00 AM CDT
America/Denver Fri, Oct 16, 2026, 9:00 AM MDT
America/Phoenix Fri, Oct 16, 2026, 8:00 AM MST
America/Los_Angeles Fri, Oct 16, 2026, 8:00 AM PDT
dateStyle + timeStyle: Friday, October 16, 2026 at 8:00 AM
dateStyle + timeZoneName -> TypeError

Use the IANA names (America/Denver), not labels like “Mountain Time”. They carry the full daylight saving history, which is why Phoenix correctly shows MST in October while Denver is on MDT. For the basics of the object itself, see working with dates in TypeScript.
The dateStyle and timeStyle shortcuts are handy, but you can’t mix them with individual fields such as timeZoneName or hour. Doing that throws a TypeError at runtime. For general patterns without zones, see my guide on how to format dates in TypeScript.
Why does my date show the wrong time zone?
When you leave out timeZone, every method falls back to the zone of whatever computer runs the code. That’s the user’s laptop in a browser, and the server’s setting in Node.js. Here’s the same order timestamp printed several ways:
const order = new Date("2026-10-16T03:30:00Z");
console.log("runtime zone: ", Intl.DateTimeFormat().resolvedOptions().timeZone);
console.log("toString(): ", order.toString());
console.log("getHours(): ", order.getHours());
console.log("toISOString(): ", order.toISOString());
console.log("no timeZone: ", order.toLocaleString("en-US"));
console.log("New York: ", order.toLocaleString("en-US", { timeZone: "America/New_York" }));
console.log("Los Angeles: ", order.toLocaleString("en-US", { timeZone: "America/Los_Angeles" }));
console.log("LA date only: ", order.toLocaleDateString("en-US", { timeZone: "America/Los_Angeles" }));
Output:
runtime zone: America/Chicago
toString(): Thu Oct 15 2026 22:30:00 GMT-0500 (Central Daylight Time)
getHours(): 22
toISOString(): 2026-10-16T03:30:00.000Z
no timeZone: 10/15/2026, 10:30:00 PM
New York: 10/15/2026, 11:30:00 PM
Los Angeles: 10/15/2026, 8:30:00 PM
LA date only: 10/15/2026

timeZone, the output follows the machine (Central here); with it, the result is the same on every computer.Notice the date flips too: 10:30 PM on October 15 in Chicago is 8:30 PM the same evening in Los Angeles, but already October 16 in UTC. That’s the classic off-by-one bug in date-only displays.
My rule: store and send dates as UTC with toISOString(), and pick the zone only at display time. If dates arrive as strings first, read how to convert a string to a date in TypeScript to parse them safely.
Methods such as getHours() and getDate() always use the machine zone. There’s no argument to change that, so don’t build zone logic on them, including when you get the week number from a date in TypeScript.
Format a date as an ISO string with a time zone offset
toISOString() only ever gives you UTC with a Z. APIs and logs often want local time plus an offset, like 2026-10-16T11:00:00-04:00. formatToParts() hands you each piece so you can assemble any layout:
type Parts = Record<string, string>;
function partsInZone(date: Date, timeZone: string): Parts {
const fmt = new Intl.DateTimeFormat("en-US", {
timeZone,
year: "numeric", month: "2-digit", day: "2-digit",
hour: "2-digit", minute: "2-digit", second: "2-digit",
hourCycle: "h23",
timeZoneName: "longOffset", // "GMT-04:00"
});
const parts: Parts = {};
for (const { type, value } of fmt.formatToParts(date)) parts[type] = value;
return parts;
}
function isoInZone(date: Date, timeZone: string): string {
const p = partsInZone(date, timeZone);
const offset = p.timeZoneName === "GMT" ? "+00:00" : p.timeZoneName.slice(3);
return `${p.year}-${p.month}-${p.day}T${p.hour}:${p.minute}:${p.second}${offset}`;
}
function abbrInZone(date: Date, timeZone: string): string {
return new Intl.DateTimeFormat("en-US", { timeZone, timeZoneName: "short" })
.formatToParts(date)
.find((p) => p.type === "timeZoneName")?.value ?? "";
}
function stampInZone(date: Date, timeZone: string): string {
const p = partsInZone(date, timeZone);
return `${p.year}-${p.month}-${p.day} ${p.hour}:${p.minute} ${abbrInZone(date, timeZone)}`;
}
const midnightEastern = new Date("2026-10-17T04:00:00Z");
for (const tz of ["America/New_York", "America/Chicago", "America/Denver", "America/Los_Angeles", "UTC"]) {
console.log(tz.padEnd(20), isoInZone(midnightEastern, tz).padEnd(26), stampInZone(midnightEastern, tz));
}
Output:
America/New_York 2026-10-17T00:00:00-04:00 2026-10-17 00:00 EDT
America/Chicago 2026-10-16T23:00:00-05:00 2026-10-16 23:00 CDT
America/Denver 2026-10-16T22:00:00-06:00 2026-10-16 22:00 MDT
America/Los_Angeles 2026-10-16T21:00:00-07:00 2026-10-16 21:00 PDT
UTC 2026-10-17T04:00:00+00:00 2026-10-17 04:00 UTC

Two details make this helper reliable:
hourCycle: "h23"prints midnight as00rather than24.timeZoneName: "longOffset"returnsGMT-04:00, which I trim to-04:00.
Zero-padding comes free from "2-digit"; for other numbers, see adding leading zeros to a number in TypeScript.
UTC is the one zone that reports plain GMT with no digits, so the helper maps it to +00:00. The same "2-digit" idea is explained in formatting numbers as two digits in TypeScript.
How daylight saving time changes the time zone offset
US offsets aren’t fixed. In 2026, daylight saving time starts on March 8 and ends on November 1, and a few places, like most of Arizona, never switch. This checks the offset around both dates:
const offsetFmt = (timeZone: string) =>
new Intl.DateTimeFormat("en-US", { timeZone, timeZoneName: "shortOffset" });
const checkDays = ["2026-03-07", "2026-03-09", "2026-10-31", "2026-11-02"];
for (const tz of ["America/Chicago", "America/Phoenix"]) {
const fmt = offsetFmt(tz);
const row = checkDays.map((d) => {
const noonUtc = new Date(`${d}T18:00:00Z`);
const zone = fmt.formatToParts(noonUtc).find((p) => p.type === "timeZoneName")?.value;
return `${d.slice(5)} ${zone}`;
});
console.log(tz.padEnd(16), row.join(" | "));
}
Output:
America/Chicago 03-07 GMT-6 | 03-09 GMT-5 | 10-31 GMT-5 | 11-02 GMT-6
America/Phoenix 03-07 GMT-7 | 03-09 GMT-7 | 10-31 GMT-7 | 11-02 GMT-7
Chicago moves from GMT-6 to GMT-5 and back, while Phoenix stays at GMT-7 all year. Never hard-code “-05:00” for Central time; let the formatter look it up. DST also explains odd 23-hour days when you calculate the date difference in days in TypeScript.
Fix TypeScript errors when you format a date with a time zone
The options object is fully typed in the es2024 library, so a wrong value for timeZoneName fails at compile time. Here I guessed “abbr” instead of “short”:
const shipped = new Date("2026-10-16T21:15:00Z");
const fmt = new Intl.DateTimeFormat("en-US", {
timeZone: "America/Denver",
hour: "numeric",
minute: "2-digit",
timeZoneName: "abbr",
});
console.log(fmt.format(shipped));
Output:
tz_typo.ts(7,3): error TS2769: No overload matches this call.
The last overload gave the following error.
Type '"abbr"' is not assignable to type '"long" | "longGeneric" | "longOffset" | "short" | "shortGeneric" | "shortOffset" | undefined'.

The timeZone option, however, is just a string. TypeScript can’t tell that “Central Standard Time” isn’t a real zone, so the mistake only shows up at runtime as a RangeError. Check user input with a small guard:
function isValidTimeZone(timeZone: string): boolean {
try {
new Intl.DateTimeFormat("en-US", { timeZone });
return true;
} catch {
return false;
}
}
for (const tz of ["America/Denver", "Central Standard Time", "US/Pacific", "EST"]) {
console.log(tz.padEnd(22), isValidTimeZone(tz));
}
try {
new Date("2026-10-16T15:00:00Z").toLocaleString("en-US", { timeZone: "Eastern Time" });
} catch (err) {
console.log((err as Error).name + ":", (err as Error).message);
}
const usZones = Intl.supportedValuesOf("timeZone").filter((z) => z.startsWith("America/"));
console.log("America/* zones this runtime knows:", usZones.length);
Output:
America/Denver true
Central Standard Time false
US/Pacific true
EST true
RangeError: Invalid time zone specified: Eastern Time
America/* zones this runtime knows: 144
Old aliases like US/Pacific and EST still work, but I’d stick to the America/... names. If you’d rather catch typos at compile time, a union of the zones you support works well; see my TypeScript key value pair guide for mapping labels to zone names.