Zeitzonen in JavaScript Verarbeiten: Intl und Luxon

The JavaScript Date Object's Timezone Problem

JavaScript's Date object always stores time as a UTC timestamp internally, but most of its methods return values in the browser's local timezone. This inconsistency causes many subtle bugs, especially when code runs in different environments.

const d = new Date("2024-03-01T14:00:00Z");  // UTC
console.log(d.getHours());        // Local hours — varies by browser location!
console.log(d.getUTCHours());     // 14 — always UTC, predictable

Using the Intl API (Built-in, No Library Needed)

The modern Intl.DateTimeFormat API lets you format dates in any timezone without external dependencies:

const d = new Date("2024-03-01T14:00:00Z");

// Format in Seoul time
const seoulFormatter = new Intl.DateTimeFormat("ko-KR", {
  timeZone: "Asia/Seoul",
  dateStyle: "full",
  timeStyle: "short",
});
console.log(seoulFormatter.format(d));
// 2024년 3월 1일 금요일 오후 11:00

// Format in New York time
const nyFormatter = new Intl.DateTimeFormat("en-US", {
  timeZone: "America/New_York",
  hour: "2-digit",
  minute: "2-digit",
  timeZoneName: "short",
});
console.log(nyFormatter.format(d));
// 09:00 AM EST

Luxon: The Modern Date Library

Luxon is the spiritual successor to Moment.js, built for immutability and first-class timezone support via the Intl API:

import { DateTime } from "luxon";

// Create from UTC ISO string
const dt = DateTime.fromISO("2024-03-01T14:00:00Z");

// Convert to Seoul timezone
const seoul = dt.setZone("Asia/Seoul");
console.log(seoul.toString());
// 2024-03-01T23:00:00.000+09:00

// Get specific parts
console.log(seoul.hour);    // 23
console.log(seoul.offset);  // 540 (minutes)

// Format
console.log(seoul.toFormat("yyyy-MM-dd HH:mm z"));
// 2024-03-01 23:00 KST

Temporal: The Future (Stage 3 Proposal)

The Temporal API is a TC39 Stage 3 proposal that will replace Date with a comprehensive, timezone-aware date library built into JavaScript itself. It distinguishes between wall-clock time (PlainDateTime), exact time (Instant), and zoned time (ZonedDateTime):

// Future API (polyfill available now)
const instant = Temporal.Now.instant();
const zonedSeoul = instant.toZonedDateTimeISO("Asia/Seoul");
console.log(zonedSeoul.toString());

Best Practices

  • Parse dates from APIs as UTC ISO strings: new Date("2024-03-01T14:00:00Z")
  • Use getUTC* methods instead of get* for consistent server-side behavior (Node.js)
  • For complex timezone logic, use Luxon or date-fns-tz
  • Never use new Date("2024-03-01") without a time — it parses as midnight UTC, which may display as the previous day in western timezones