Timezone Mastery
Timezones are the single biggest source of time-related bugs. Most "time bugs" are actually timezone bugs in disguise.
IANA Timezone Database
The authoritative source for timezone information is the IANA Time Zone Database (also called "tz database" or "Olson database"). It uses geographic identifiers like America/Los_Angeles, Europe/London, or Asia/Tokyo.
Always use IANA identifiers, never abbreviations. PST is ambiguous — it could mean Pacific Standard Time (US), Philippine Standard Time, or Pakistan Standard Time. America/Los_Angeles is unambiguous and automatically handles daylight saving transitions.
IANA identifiers follow the pattern Area/Location where Area is a continent or ocean (America, Europe, Asia, Pacific) and Location is a major city in that timezone. Some have three parts: America/Argentina/Buenos_Aires or America/Indiana/Indianapolis for regions with unique timezone histories.
Offsets Are Not Timezones
A UTC offset like -08:00 describes the current difference from UTC but contains no information about DST rules or historical changes. Los Angeles is -08:00 in winter (PST) but -07:00 in summer (PDT). If you store only the offset, you lose the ability to correctly handle times across DST transitions.
Store timezone identifiers, not offsets. When you need an offset for display or API calls, derive it from the identifier at the specific moment in time.
Daylight Saving Time
DST transitions create two kinds of problems: gaps and overlaps.
During spring forward, clocks skip an hour. In the US Pacific timezone on March 10, 2024, clocks jumped from 1:59:59 AM directly to 3:00:00 AM. The time 2:30 AM did not exist that day. Scheduling a meeting for 2:30 AM on that date is impossible — the time is invalid.
During fall back, clocks repeat an hour. On November 3, 2024, clocks went from 1:59:59 AM back to 1:00:00 AM. The time 1:30 AM occurred twice. If someone says "1:30 AM on November 3rd," which 1:30 AM do they mean? Without additional context, it's ambiguous.
Not all regions observe DST. Arizona (except Navajo Nation) doesn't observe DST and stays on Mountain Standard Time year-round. Most of the tropics don't observe DST. Some countries have changed DST rules — Russia abolished DST in 2011, re-introduced it in 2014, then abolished it again.
DST transition dates vary by country and change occasionally. The US shifted DST dates in 2007. Egypt has suspended, restored, and re-suspended DST multiple times. Never hardcode DST transition dates — rely on the IANA database.
UTC as Canonical Storage
Store timestamps in UTC. Always. Convert to local timezone only for display.
UTC (Coordinated Universal Time) has no DST transitions, no political timezone changes, and no ambiguity. A UTC timestamp represents exactly one instant in time. When you need to display that time to a user, convert it to their timezone at display time.
This pattern separates concerns: storage handles the "when" (UTC instant), display handles the "how" (user's preferred format and timezone). If a user moves from New York to Los Angeles, their stored events don't need modification — only the display timezone changes.
User Timezone Context
Every time-aware application needs to know the user's timezone. Get it explicitly — asking "What timezone are you in?" or detecting from their calendar/profile settings. Browser detection (Intl.DateTimeFormat().resolvedOptions().timeZone) gives the device timezone, which may not be the user's working timezone.
Store the user's timezone preference in their profile. Reference it when interpreting relative times like "tomorrow" or "9 AM" — these phrases only make sense in a specific timezone.
When calling APIs on behalf of a user, use their timezone for queries like "events today" or "emails from this morning." Google Calendar's API, for example, accepts a timeZone parameter that affects how date boundaries are interpreted.
Common Pitfalls
JavaScript's new Date() without arguments returns the current time in the local timezone of wherever the code runs. In a browser, that's the user's device. On a server, it's the server's timezone (often UTC, but not always). Never assume. For server code, explicitly construct UTC times.
Date.parse() and new Date(string) have inconsistent behavior across implementations. The string 2024-01-15 might be parsed as midnight UTC or midnight local time depending on the JavaScript engine. For reliable parsing, use a library or construct dates from explicit components.
Comparing dates across timezones requires converting to a common reference. "January 15, 2024 in Tokyo" starts 17 hours before "January 15, 2024 in Los Angeles." If you're checking whether two events are "on the same day," you must specify whose day — the same instant can be Monday in Tokyo and Sunday in LA.
Recurring events spanning DST transitions need special handling. A weekly meeting at "9 AM" should stay at 9 AM local time even when DST changes. This means the UTC time shifts by an hour. Most calendar systems handle this correctly, but be careful when building custom recurrence logic.