Timezone Conversion in JavaScript — Intl API, date-fns-tz, and Luxon
Convert dates between timezones in JavaScript using the Intl.DateTimeFormat API, date-fns-tz, and Luxon. Covers UTC offset vs named timezone, DST handling, user timezone...
Use the tool
Timestamp Converter
Convert Unix timestamps, epoch seconds/milliseconds, and ISO 8601 dates.
JavaScript’s Intl API supports IANA timezone names for display. For arithmetic across timezones, use date-fns-tz or Luxon which properly handle DST transitions.
Convert between timezone-aware timestamps with the Timestamp Converter.
UTC offset vs named timezone
// ❌ UTC offset: doesn't handle DST
const offset = new Date('2026-05-12T14:30:00-05:00');
// In November, UTC-5 is still UTC-5 — but CST actually becomes UTC-6 in summer
// ✅ IANA timezone name: correctly handles DST
// Use 'America/Chicago' instead of '-05:00'
// The system knows when DST switches occur
IANA timezone names look like America/New_York, Europe/London, Asia/Tokyo.
Display a date in any timezone (Intl API)
const date = new Date('2026-05-12T14:30:00Z');
function formatInTimezone(date, timezone, locale = 'en-US') {
return new Intl.DateTimeFormat(locale, {
timeZone: timezone,
dateStyle: 'full',
timeStyle: 'long',
}).format(date);
}
formatInTimezone(date, 'America/New_York');
// "Tuesday, May 12, 2026 at 10:30:00 AM EDT"
formatInTimezone(date, 'Europe/London');
// "Tuesday, May 12, 2026 at 3:30:00 PM BST"
formatInTimezone(date, 'Asia/Tokyo');
// "Wednesday, May 13, 2026 at 11:30:00 PM JST"
// Format parts separately:
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'America/Los_Angeles',
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', timeZoneName: 'short',
});
formatter.format(date); // "05/12/2026, 07:30 AM PDT"
date-fns-tz: convert for arithmetic
npm install date-fns date-fns-tz
import { toZonedTime, fromZonedTime, formatInTimeZone } from 'date-fns-tz';
import { addDays, addHours } from 'date-fns';
const utcDate = new Date('2026-05-12T14:30:00Z');
// Convert to a zoned time (for display/arithmetic in that timezone):
const nyDate = toZonedTime(utcDate, 'America/New_York');
// nyDate represents 2026-05-12 10:30:00 in New York's clock time
// Format in timezone:
formatInTimeZone(utcDate, 'America/New_York', 'yyyy-MM-dd HH:mm zzz');
// "2026-05-12 10:30 EDT"
formatInTimeZone(utcDate, 'Asia/Kolkata', 'yyyy-MM-dd HH:mm zzz');
// "2026-05-12 20:00 IST"
// Add days in a timezone (important for DST!):
const nyMidnight = toZonedTime(new Date('2026-03-08T05:00:00Z'), 'America/New_York');
// 2026-03-08 is the night of US DST spring-forward
const nextDay = addDays(nyMidnight, 1); // Correctly handles the 23-hour day
const backToUtc = fromZonedTime(nextDay, 'America/New_York');
Luxon: most complete timezone support
import { DateTime } from 'luxon';
// UTC to timezone:
const utc = DateTime.fromISO('2026-05-12T14:30:00Z');
const ny = utc.setZone('America/New_York');
ny.toFormat('yyyy-MM-dd HH:mm zzz'); // "2026-05-12 10:30 EDT"
// Create in a specific timezone:
const tokyoNoon = DateTime.fromObject(
{ year: 2026, month: 5, day: 12, hour: 12, minute: 0 },
{ zone: 'Asia/Tokyo' }
);
tokyoNoon.toUTC().toISO(); // UTC equivalent
// List all timezones a user might pick:
// Luxon doesn't provide this directly — use Intl.supportedValuesOf
const timezones = Intl.supportedValuesOf('timeZone');
// ['Africa/Abidjan', 'Africa/Accra', ... 'UTC', ...]
Detect user’s timezone
// Get IANA name of user's local timezone:
const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
// "America/New_York", "Europe/Berlin", etc.
// Display current time in user's timezone:
function displayLocalTime(utcTimestamp) {
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
return new Intl.DateTimeFormat('en-US', {
timeZone: tz,
dateStyle: 'medium',
timeStyle: 'short',
}).format(new Date(utcTimestamp));
}
Server-side: Node.js timezone handling
// Node.js uses the OS timezone by default
// Always work with UTC on the server and convert for display:
function utcToTimezone(utcDate, timezone) {
const formatter = new Intl.DateTimeFormat('en-CA', {
timeZone: timezone,
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', second: '2-digit',
hour12: false,
});
const parts = Object.fromEntries(
formatter.formatToParts(utcDate).map(({ type, value }) => [type, value])
);
return `${parts.year}-${parts.month}-${parts.day}T${parts.hour}:${parts.minute}:${parts.second}`;
}
utcToTimezone(new Date('2026-05-12T14:30:00Z'), 'Asia/Tokyo');
// "2026-05-12T23:30:00"
Common pitfalls
// ❌ Don't use getTimezoneOffset() for named timezone conversion:
new Date().getTimezoneOffset(); // Returns LOCAL timezone offset, not arbitrary ones
// ❌ Don't construct dates with local time then convert:
new Date(2026, 4, 12, 14, 30); // Uses local timezone, not UTC
// ✅ Always start from UTC:
new Date('2026-05-12T14:30:00Z');
// or
new Date(Date.UTC(2026, 4, 12, 14, 30, 0));
Related tools
- Timestamp Converter — convert timestamps online
- JSON Formatter — inspect date fields in API responses
- URL Encoder — encode datetime strings safely
Related posts
- Unix Timestamps and Timezones: What Can Go Wrong — Unix timestamps are always UTC — but displaying, parsing, and storing them is wh…
- Date to Unix Timestamp — Convert Any Date to Unix Time — Converting a date to a Unix timestamp gives you the seconds since January 1, 197…
- Epoch Time Explained — What Is Unix Epoch and Why Does It Start January 1, 1970? — Unix epoch time counts seconds from January 1, 1970 00:00:00 UTC. Learn why 1970…
- ISO 8601 Date Format — The Standard for Dates in APIs and Databases — ISO 8601 is the international standard for representing dates and times. Learn t…
- JavaScript Date Formatting — Intl.DateTimeFormat, date-fns, and Custom Formats — Format dates in JavaScript using the built-in Intl.DateTimeFormat API, date-fns …
Related tool
Timestamp Converter
Convert Unix timestamps, epoch seconds/milliseconds, and ISO 8601 dates.
Written by Mian Ali Khalid. Part of the Dev Productivity pillar.