Time seems simple until you try to write software that handles it correctly. An event scheduled for "3 PM" means different things in Tokyo, London, and New York. Daylight saving time shifts clocks forward or backward — but not everywhere, and not on the same dates. A timestamp that looks correct in your local environment breaks in production because the server is in a different zone.
Time zones are one of the most reliably confusing topics in software development. This article explains how they work, where the complexity comes from, and how to avoid the most common mistakes.
Why time zones exist
The Earth rotates 360 degrees in 24 hours, which means the sun is at its highest point at different moments depending on your longitude. Before the 19th century, every city set its clocks to local solar time — noon was when the sun was overhead. This worked fine until railroads connected distant cities and a train schedule needed a single, consistent clock.
In 1884, delegates from 25 nations met at the International Meridian Conference in Washington, D.C. and agreed to divide the world into 24 standard time zones, each offset from the prime meridian (0 degrees longitude) passing through Greenwich, England.
In practice, time zone boundaries follow political borders, not neat longitudinal lines. China spans five geographical zones but uses a single official time (UTC+8). India uses UTC+5:30 — a half-hour offset. Nepal uses UTC+5:45. The real-world map of time zones is messy.
UTC vs. GMT
GMT (Greenwich Mean Time) is the mean solar time at the Royal Observatory in Greenwich. It was the world's time reference for over a century.
UTC (Coordinated Universal Time) replaced GMT as the international standard in 1972. UTC is based on atomic clocks rather than astronomical observation, making it far more precise. For most practical purposes, UTC and GMT show the same time, but UTC is the correct technical reference.
Why "UTC" and not "CUT"? The abbreviation is a compromise between the English "Coordinated Universal Time" (CUT) and the French "Temps Universel Coordonné" (TUC). Neither side got their preferred acronym, so UTC was chosen as a language-neutral alternative.
Daylight saving time: organized chaos
About 70 countries observe daylight saving time (DST), shifting clocks forward by one hour in spring and back in autumn. The intent is to align waking hours with daylight. The result is a semiannual source of bugs.
Key complications:
- Not universal. Most of Africa, Asia, and South America do not observe DST. Within the US, Arizona and Hawaii opt out.
- Different dates. The EU shifts on the last Sunday of March and October. The US shifts on the second Sunday of March and the first Sunday of November. They are out of sync for several weeks each year.
- Ambiguous times. When clocks fall back, the hour from 1:00 AM to 2:00 AM occurs twice. A timestamp of "1:30 AM" on that day is ambiguous.
- Skipped times. When clocks spring forward, the hour from 2:00 AM to 3:00 AM does not exist. A meeting scheduled at 2:30 AM on that day never happens.
- Political changes. Governments can (and do) change DST rules with little notice. Russia adopted permanent DST in 2011, then switched to permanent standard time in 2014. Morocco has changed DST rules multiple times.
ISO 8601: the universal date format
To avoid ambiguity, the international standard ISO 8601 defines a clear date and time format:
2026-03-29T14:30:00Z
2026-03-29T14:30:00+02:00
2026-03-29T14:30:00-05:00
- The
Tseparates date from time. Zmeans UTC (the "Zulu" time zone in military terminology).+02:00or-05:00is the UTC offset.
This format is unambiguous, sortable as plain text, and universally understood by date parsing libraries. When in doubt, use ISO 8601.
Unix timestamps
A Unix timestamp (also called epoch time or POSIX time) is the number of seconds that have elapsed since January 1, 1970, 00:00:00 UTC — a moment known as the Unix epoch.
| Human-readable | Unix timestamp |
|---|---|
| 1970-01-01 00:00:00 UTC | 0 |
| 2000-01-01 00:00:00 UTC | 946684800 |
| 2026-03-29 12:00:00 UTC | 1774987200 |
Unix timestamps have no time zone — they are always in UTC. This makes them ideal for storing and comparing times in software. You convert to a local time zone only at the display layer.
The Year 2038 problem: Systems that store Unix timestamps as a 32-bit signed integer will overflow on January 19, 2038 at 03:14:07 UTC. The maximum value (2,147,483,647) rolls over to a negative number, interpreted as December 1901. Most modern systems use 64-bit integers, which will not overflow for another 292 billion years.
The IANA time zone database
Software does not just need UTC offsets — it needs to know the full history and future rules for each region, including DST transitions, political changes, and historical anomalies. This information lives in the IANA Time Zone Database (also called the Olson database or tzdata).
It uses identifiers like America/New_York, Europe/Paris, Asia/Tokyo. Each entry encodes the complete history of UTC offsets and DST rules for that location.
This is why you should never store a time zone as a fixed offset like "+02:00". An offset tells you the current difference from UTC but says nothing about DST rules. Europe/Paris is UTC+1 in winter and UTC+2 in summer. The IANA identifier captures both.
Common bugs in software
- Storing local time without a time zone. A value like
2026-03-29 14:30:00is meaningless without knowing which time zone it refers to. Always store UTC or include the offset. - Assuming UTC offset equals time zone. UTC+2 in March might be UTC+3 in July (if the region observes DST). Store the IANA identifier, not the offset.
- Ignoring DST transitions in scheduling. A daily job at 2:30 AM will skip once a year and run twice once a year if you are not handling DST.
- Assuming days have 24 hours. On DST transition days, a day has 23 or 25 hours. Calculating "tomorrow at the same time" by adding 86,400 seconds will be off by an hour.
- Using JavaScript
Datenaively.new Date("2026-03-29")is parsed as UTC in some engines and as local time in others. Always be explicit about the time zone.
Best practices for developers
- Store times in UTC. Convert to the user's local time zone only at the presentation layer.
- Use IANA time zone identifiers (
America/New_York), not fixed offsets (-05:00). - Use ISO 8601 for serialization. It is unambiguous and universally parseable.
- Use a mature date library. In JavaScript, use
Intl.DateTimeFormator a library likedate-fns-tz. In Python, usezoneinfo(3.9+) orpytz. In Java, usejava.time.ZonedDateTime. - Keep
tzdataupdated. Governments change DST rules. Your operating system and language runtime need current timezone data. - Test with multiple time zones. Do not assume your server and your users share the same zone.
Going further
Time is deceptively complex, but the rules are well-documented and the tools are mature. The key is to respect the complexity rather than assuming it away.
- Cron Expressions Demystified — scheduling tasks across time zones
- Hash Generator and Regex Tester — more developer tools on ToolK