Development

Time zones, UTC and daylight saving: why dates break in production

Almost every date bug is born from the same confusion: treating UTC, GMT, offset and time zone as if they were the same thing. They are not. A meeting set "for 9 a.m." can land an hour earlier six months later; a local time can simply fail to exist; a "1:30 a.m." can happen twice in one night. This guide separates the four concepts, shows why the America/Sao_Paulo identifier beats the −03:00 offset, works two numeric conversions to their explicit result and closes with the golden rule of storage. Test what you read in the [time zone converter](tool:conversor-fuso-horario) and read the timestamp of an instant in the [timestamp converter](tool:conversor-timestamp).

J-Kit12 min readIntermediate
  • Time zones
  • UTC
  • Date and time
  • Development

Key takeaways

  • UTC is a time scale (the ruler), not a zone; GMT is the legacy zone/scale it grew out of.
  • An offset (−03:00) is a snapshot of one instant; an IANA zone (America/Sao_Paulo) is the rulebook, which changes by law.
  • Store instants in UTC; store the IANA zone (not the offset) for future recurring events; test on the daylight-saving transitions.

UTC is not GMT, and offset is not zone

Four words that look like synonyms are the root of nearly every date bug. UTC (Coordinated Universal Time) is a time scale, the ruler the world measures instants against. GMT (Greenwich Mean Time) is the historical ancestor of that ruler, used today as a legacy zone (the United Kingdom’s in winter). An offset is just a numeric shift relative to UTC, like −03:00. And an IANA zone, like America/Sao_Paulo, is a living rule: it says which offset applies on each date, including whether, when and for how long there is daylight saving. Confusing the ruler, the number and the rulebook is where most calendars and schedulers break.

UTC
The reference civil time scale. It has no location: 12:00 UTC is the same instant across the whole planet.
GMT
Greenwich Mean Time. Historically the basis of world time; today, in practice, a zone name (UTC+00:00 in British winter). For engineering, prefer to say UTC.
TAI
International Atomic Time: the uninterrupted count of atomic seconds, with no adjustments. UTC is TAI minus a whole number of leap seconds.
Offset
The difference between a local clock and UTC at one instant, like −03:00 or +05:30. It is a fact about one moment, not about the future.
IANA zone
An Area/Location identifier (America/Sao_Paulo) carrying a region’s past and future offset rules, daylight saving included.

The distinction that matters most is offset versus zone. −03:00 says where the clock is now; America/Sao_Paulo says where it will be next year, even if the law changes. Storing an offset is taking a photo; storing a zone is storing the rules that generate every photo, past and future. That difference is the axis of everything that follows.

A short history of coordinated time

  1. 1884International Meridian Conference

    In Washington, 25 nations pick the Greenwich meridian as zero longitude; GMT becomes the world time standard.

  2. 1960International time coordination

    Worldwide coordination of time and frequency broadcasts begins, the embryo of UTC.

  3. 1967The second turns atomic

    The SI second is defined by the caesium atom; "UTC" settles in as the official abbreviation.

  4. 1972UTC takes its current form

    UTC starts stepping in whole 1-second jumps over TAI; the first leap second is inserted on 30 June 1972.

  5. 1980sThe zone database is born

    The time zone database (tz / zoneinfo) appears, gathering each region’s local history; today it is maintained by IANA.

  6. 2019Brazil ends daylight saving

    Decree No. 9,772 of 25 April 2019 revokes daylight saving; the last period applied had ended in February 2019.

  7. 2022Retiring the leap second

    Resolution 4 of the 27th CGPM decides to let the UT1−UTC difference grow: leap seconds are to be discontinued by or before 2035.

UTC is TAI plus a whole number of leap seconds, inserted irregularly to keep the civil clock within about 0.9 second of the Earth’s actual rotation (UT1). Because the Earth does not spin at a predictable rate, those jumps could never be scheduled years ahead, which torments any system that assumes every minute has exactly 60 seconds. That is why the General Conference on Weights and Measures (CGPM) decided in 2022 to stop inserting leap seconds by 2035, accepting that UTC will drift a little further from the Earth’s rotation in exchange for predictability.

The IANA database and why America/Sao_Paulo

Every operating system, language and runtime consults the same source of truth about zones: the IANA time zone database (also called tz, zoneinfo or the Olson database). Its identifiers follow the Area/Location pattern, a continent or ocean plus the largest representative city: America/Sao_Paulo, Europe/London, Asia/Tokyo. Why the city and not "BRT"? Because the abbreviation is ambiguous (is BST British Summer Time or Bangladesh Standard Time?) and, worse, it encodes the current offset, which changes. A city name is politically stable and lets the database attach the region’s full history and future rules to it. When a government changes the rules, as Brazil did in 2019, a new tzdata release ships and every updated system learns the new behavior. Releases have no fixed calendar: they come out when politics forces them, often several times a year, named 2025a, 2025b, and so on.

An example makes the offset-versus-zone difference concrete. São Paulo (America/Sao_Paulo) is at UTC−3 all year now, since it no longer has daylight saving. New York (America/New_York) alternates: UTC−4 in the Northern Hemisphere summer (EDT) and UTC−5 in winter (EST). Set a recurring call "at 9 a.m. in São Paulo" and watch what happens to New York’s clock as the season turns:

The same "9 a.m. in São Paulo" lands on different New York times, because New York’s offset changes and São Paulo’s does not.
DateSão Paulo (UTC−3)In UTCNew York
15 Jul 202509:0012:0008:00 (EDT, UTC−4)
15 Jan 202509:0012:0007:00 (EST, UTC−5)

The middle step is always the same: 09:00 in São Paulo is 09:00 + 3 = 12:00 UTC on both days. What changes is the trip back: in July, New York is at −4 from UTC, so 12:00 − 4 = 08:00; in January it is at −5, so 12:00 − 5 = 07:00. The gap between the two cities is 1 hour in the Northern summer and 2 hours in winter. Had you stored only the offset from the first reading, the second would come out wrong. Convert any pair of cities in the time zone converter below.

Convert a time across zones and see the daylight-saving effect in real time.Open the tool full page

ISO 8601, RFC 3339 and the instant on the wire

To travel between systems, a time has to name an instant unambiguously, and that requires a zone reference. ISO 8601 is the date-and-time format standard; RFC 3339 is its lean internet profile, adopted in APIs, logs and JSON. A string is an instant only if it carries an offset or the Z suffix. Here are the parts:

2025-07-15T09:00:00-03:00      <- RFC 3339 / ISO 8601

  2025-07-15   date    (ano-mes-dia / year-month-day)
  T            separator between date and time
  09:00:00     local time (hh:mm:ss)
  -03:00       offset from UTC (3 h behind)

= 2025-07-15T12:00:00Z         same instant; Z = UTC = +00:00
An annotated ISO 8601 timestamp. Z (for "Zulu") is simply +00:00.

Here lies the reason offset ≠ zone: from the string 2025-07-15T09:00:00−03:00 you know the instant, but not the rules. You cannot tell whether that clock will read −03:00 in January, because you do not know if it is São Paulo (fixed offset) or a zone with daylight saving. An offset is a fact about a single moment; a zone is the rulebook that projects the future. To reconstruct future local times, then, you need the IANA identifier, not a number.

A second example, now starting from a Unix timestamp, the seconds elapsed since 1970-01-01T00:00:00Z. A log records the value 1,700,000,000. Converting, we reach 2023-11-14T22:13:20Z. That instant is unique; what changes is the wall clock in each place:

22:13:20 (Nov 14)UTC, the canonical instant
19:13:20 (Nov 14)São Paulo (UTC−3): 22:13 − 3
07:13:20 (Nov 15)Tokyo (UTC+9): 22:13 + 9, next day

Notice that in Tokyo the date rolls forward to November 15: adding 9 hours to 22:13 crosses midnight. It is the same instant, 1,700,000,000 on the wire, seen by three clocks. Read any Unix timestamp in the timestamp converter and do duration arithmetic in the hours calculator.

What about the year 2038 problem?

Systems that store Unix time in a signed 32-bit integer only reach 2,147,483,647 (2³¹ − 1) seconds after the epoch. That limit is hit at 03:14:07 UTC on 19 January 2038; the next second, the counter overflows and "wraps" to 20:45:52 UTC on 13 December 1901.

The fix is to store in 64 bits, which pushes the overflow billions of years out, or to use an ISO 8601 string. It is worth auditing embedded and legacy systems that are rarely updated; they are the most exposed.

Times that do not exist and that happen twice

Where daylight saving still exists, each transition creates an anomaly in the local clock. In the United States, the rule in force since 2007 is: spring forward at 2 a.m. on the second Sunday of March and fall back at 2 a.m. on the first Sunday of November. In 2025, that fell on March 9 and November 2. On those two dates, local time stops being a continuous line.

The two daylight-saving anomalies, with America/New_York examples in 2025.
TransitionWhat happens to the clockExample (2025)
Spring forwardJumps from 02:00 straight to 03:00; the hour in between does not exist.Mar 9: 02:30 never happens.
Fall backFalls from 02:00 back to 01:00; the hour in between occurs twice.Nov 2: 01:30 happens twice.

The consequences are practical and unpleasant. A job scheduled for 02:30 on the spring transition day may never fire; on the fall transition day, it may fire twice. The string "2025-11-02 01:30" in America/New_York is genuinely ambiguous, it maps to two different UTC instants (one still in EDT, one already in EST), and the parser needs a rule to choose. This is exactly why wall-clock schedulers demand care at transitions; if you define jobs with cron expressions, test their behavior on those two days of the year.

The golden rule of storage

Put it all together and the storage strategy resolves itself. There are two models, and the choice between them decides whether your system survives a change of law. Store the instant in UTC plus the IANA zone, or store the local time plus an offset, but the two are not interchangeable:

Store UTC + IANA zone

  • The UTC instant never changes meaning, even if the zone’s law changes.
  • The identifier (America/Sao_Paulo) rebuilds the correct local time on any date.
  • Display becomes a calculation; the stored value is canonical and future-proof.

Store local time + offset

  • The offset freezes a snapshot: if the rule changes, the recomputed time comes out wrong.
  • It cannot tell the two "01:30" of fall apart; it is ambiguous by construction.
  • It only serves to record what a clock read at that instant, not to schedule the future.
  • Store instants (logs, "created at", "paid at") in UTC, ideally as timestamp with time zone or an ISO 8601 string with Z.
  • Store the IANA zone identifier (America/Sao_Paulo), not the offset, when the event is future and recurring.
  • Never store only the offset (−03:00) for a future event: it cannot rebuild rules that may still change by law.
  • Use the user’s zone only in the display layer; convert at the edge and keep UTC at the core.
  • Test the system exactly on the daylight-saving transitions, the hour that does not exist and the one that happens twice.
  • Prefer 64 bits (or an ISO string) over 32-bit Unix seconds, to avoid the 2038 overflow.

Frequently asked questions

Are UTC and GMT the same thing?
In everyday use the clocks coincide, but they are not the same kind of thing. UTC is a time scale, the reference ruler, defined by atomic clocks. GMT is the historical standard that preceded it and today works as a zone name (UTC+00:00 in British winter). For engineering, use and write UTC.
Should I store dates in UTC or local time?
Store instants in UTC and convert to the user’s zone only for display. The one exception is a future recurring event, which needs the IANA zone identifier (America/Sao_Paulo) with it, never a fixed offset, so it can be recomputed if the zone’s law changes.
Why use America/Sao_Paulo instead of "BRT" or −03:00?
Because the abbreviation is ambiguous and the offset is a snapshot that ages. The IANA identifier carries the zone’s full history and future rules; if the law changes, as with the end of Brazilian daylight saving in 2019, a tzdata update fixes the behavior across every system at once.
What is the year 2038 problem?
Systems that store Unix time in a signed 32-bit integer overflow at 03:14:07 UTC on 19 January 2038 and "wrap" back to 1901. The fix is to use 64 bits, which postpones the overflow by billions of years, or to store the date as an ISO 8601 string. The most exposed are embedded and legacy systems that are rarely updated.
Does Brazil still have daylight saving time?
Not since Decree No. 9,772 of 25 April 2019, which revoked daylight saving; the last period applied ended in February 2019. Even so, your system still has to handle zones that change, such as those of the United States and Europe, which is why zone and transition logic remains indispensable.

UTC is the ruler, the offset is a snapshot and the IANA zone is the rulebook. Store instants in UTC, store America/Sao_Paulo (not −03:00) when the event is future and recurring, and never reduce a recurring commitment to a single instant. Test your code on the daylight-saving transitions, the hour that does not exist and the one that happens twice, and dates stop breaking in production.

Sources & references

  1. RFC 3339, Date and Time on the Internet: Timestamps
  2. IANA, Time Zone Database (tz / zoneinfo)
  3. BIPM, Resolution 4 of the 27th CGPM (2022), on the future of UTC
  4. Brazilian Chamber of Deputies, Decree No. 9,772 of 25 April 2019
  5. The Open Group, POSIX, “Seconds Since the Epoch” (§4.16)
  6. Protocols of the International Meridian Conference (Washington, 1884)