The trap
Two cities. Both currently showing the same UTC offset. Same temperature, similar latitude. You'd think you could treat them interchangeably for timezone purposes. But six months from now, one of them will be an hour ahead of the other.
The classic example: Sydney and Brisbane, Australia.
| Date | Sydney | Brisbane | Offset diff |
|---|---|---|---|
| 2026-01-15 (summer) | UTC+11 (AEDT) | UTC+10 (AEST) | 1 hour |
| 2026-05-15 (autumn) | UTC+10 (AEST) | UTC+10 (AEST) | 0 hours |
| 2026-07-15 (winter) | UTC+10 (AEST) | UTC+10 (AEST) | 0 hours |
| 2026-10-15 (spring) | UTC+11 (AEDT) | UTC+10 (AEST) | 1 hour |
New South Wales (Sydney) observes daylight saving time. Queensland (Brisbane) does not. Half the year they're the same. Half the year they're not.
If your code uses fixed offsets — UTC+10 — to represent either city, you have a bug. The bug is silent for half the year. It surfaces on the first Sunday in October when NSW jumps to summer time and Queensland doesn't.
This isn't just an Australian quirk
Same pattern exists all over the world:
- Arizona vs the rest of Mountain Time: most of the US Mountain zone (Denver, Salt Lake City) observes DST. Arizona doesn't. So
America/Phoenixis permanently UTC-7, whileAmerica/Denveralternates between UTC-7 and UTC-6. - Hawaii and Alaska: Hawaii doesn't observe DST. Alaska does. They share zone abbreviations sometimes but are distinct timezones.
- Saskatchewan vs the rest of Central Canada: Saskatchewan stays on Central Standard Time year-round; Manitoba and surrounding provinces switch to Central Daylight Time in summer.
- European exceptions: Iceland, parts of Russia, and Belarus don't observe DST while most of Europe does.
- Crimea: changed from Ukrainian (EET, observes DST) to Russian (MSK, doesn't) in 2014. Same physical location, different timezone behavior depending on which jurisdiction you recognize.
The pattern: UTC offset is not a timezone. A timezone has rules. Those rules can change with DST, with government decisions, with anything.
Why this is a code smell
Here's what you'll see in code that has this bug:
// BAD: storing offset
const userPref = {
utcOffset: -480 // PST = UTC-8
};
function userLocalTime() {
return new Date(Date.now() + userPref.utcOffset * 60 * 1000);
}
This code works correctly in PST. It silently produces times an hour wrong from March to November (PDT) because the offset doesn't update for daylight saving.
The fix:
// GOOD: storing IANA timezone name
const userPref = {
timezone: "America/Los_Angeles"
};
function userLocalTime() {
return new Intl.DateTimeFormat('en-US', {
timeZone: userPref.timezone,
dateStyle: 'short',
timeStyle: 'long'
}).format(new Date());
}
America/Los_Angeles is an IANA timezone name. It encodes the rules. Specifically, it says: this zone is UTC-8 in standard time, UTC-7 in daylight saving, with the transition happening at 2 AM local time on the second Sunday in March and the first Sunday in November (in the current law — the rules have changed over time and the IANA database tracks the historical changes).
The IANA database is doing a lot of work for you
The IANA Time Zone Database is an open-source project that tracks every timezone rule change since the late 19th century. It's updated several times a year as countries change their DST rules. For example:
- 2007: the US extended daylight saving time by several weeks. The IANA database was updated.
- 2011: Samoa skipped Friday, December 30 entirely when it moved from west of the international date line to east of it. The IANA database was updated.
- 2019: the EU voted to end DST switches starting in 2021. The plan was postponed; the IANA database tracks current behavior.
- 2022: Mexico abolished DST in most states. The IANA database was updated within weeks.
Every time a country changes its DST rules, the IANA database gets a new release. Operating systems and language runtimes incorporate the new database. Your code, if it uses IANA timezone names, gets the new rules automatically. If you've baked in fixed offsets, you have to chase the updates yourself.
Where IANA names live
Different platforms expose this in different ways:
- JavaScript:
Intl.DateTimeFormatoptions accepttimeZone: "Asia/Tokyo" - Python:
zoneinfo.ZoneInfo("Asia/Tokyo")(Python 3.9+) - Java:
ZoneId.of("Asia/Tokyo")(java.time, Java 8+) - Go:
time.LoadLocation("Asia/Tokyo") - Rust: chrono-tz crate,
chrono_tz::Asia::Tokyo - SQL (PostgreSQL):
SELECT now() AT TIME ZONE 'Asia/Tokyo' - Linux:
TZ="Asia/Tokyo" dateat the shell
All of them are pulling from the same underlying IANA database (or a recent snapshot of it).
The remaining trap: timezone aliases
The IANA database includes aliases — backward-compatible names that point to other zones. Examples:
US/Pacific→ alias forAmerica/Los_AngelesAsia/Calcutta→ alias forAsia/Kolkata(renamed in 1993 to match the city's modern name)Europe/Kiev→ alias forEurope/Kyiv(renamed in 2022)
The aliases still work, but new code should use the canonical names. If your database has old data with Asia/Calcutta, leave it — converting unnecessarily can introduce bugs. The aliases are guaranteed to keep working.
The takeaway
- UTC offset ≠ timezone. Offset is a snapshot; timezone is a rule.
- Always use IANA names for any persisted timezone reference.
America/New_York, notESTor-05:00. - Be specific about cities. Two cities with the same current offset can diverge in 6 months.
- Trust your platform's IANA library. Don't reimplement DST math. Almost everyone who tries gets it wrong.
- Update your IANA database. If your system hasn't received OS updates in a couple of years, your timezone rules are out of date.
Browse the full IANA timezone list in the timezone dropdown of the main converter — 418 named zones, all properly DST-aware.
Published April 13, 2026. Tagged: timezones, dst, iana.