If you’ve ever shipped a feature that “worked on your machine” but broke for a user in another country, there’s a decent chance time was involved.

Developers don’t struggle with time because we can’t count minutes. We struggle because software has to represent several different things that people casually call “time”:

  • An instant (a specific moment in the world)
  • A local clock display (what a wall clock shows somewhere)
  • A calendar date (which can flip differently depending on the time zone)
  • A duration (how long something took)
  • A recurring schedule (every Monday at 9 AM, even across DST)

This guide is the practical playbook teams use to avoid the classic bugs: DST surprises, ambiguous timestamps, broken reminders, and reports that don’t match between regions.

The rule most teams eventually adopt

Store instants in UTC. Store the user’s time zone separately. Convert only at the edges.

Start by Naming the Thing You’re Storing

Before choosing a datatype, ask: what is this value?

What you mean Example How to store
Instant “Order was paid at this moment” UTC timestamp
Local wall time “Store opens at 09:00” Local time + IANA zone
Date only “User’s birthday: 1998‑04‑09” Date type, no time zone
Duration “Timeout after 30 seconds” Duration type; monotonic clock
Recurring schedule “Every weekday at 09:30 in Berlin” Local time + zone + recurrence rule

The mistakes begin when you store a wall time as if it were an instant, or store an instant but later display it without the correct time zone context.

UTC Is the Backbone (GMT Is the History Lesson)

UTC is the modern coordination standard because it avoids the political and seasonal changes that affect local time zones. It does not use DST, and it travels cleanly across services.

If you want the deeper context, see The History of GMT and UTC and Mastering UTC.

Use IANA Time Zones, Not Abbreviations

If you store a user’s time zone, store an IANA ID like America/Los_Angeles, Europe/London, or Asia/Shanghai.

Abbreviations like CST and IST are ambiguous. Even when they’re not ambiguous in your team’s culture, they’re ambiguous on the internet—and software lives on the internet.

What to store

Good: America/New_York
Risky: EST
Worse: “New York time” as a free-text string

DST Creates Gaps and Overlaps (and That’s Where Bugs Live)

Daylight saving time produces two edge cases that break naive scheduling:

  • Spring forward gap: some local times do not exist.
  • Fall back overlap: some local times happen twice.

If you schedule “2026‑03‑08 02:30 America/Los_Angeles,” what does that mean? On some DST transitions, that local time is invalid. Good systems define an explicit rule: move forward to the next valid time, or reject with a clear error.

For the human side of DST (why it exists and why people argue about it), see The Story Behind Daylight Saving Time.

Parsing and Formatting: Be Strict on Input, Clear on Output

One reason time bugs are expensive is that strings are slippery. The simplest survival strategy is:

  • Accept: ISO 8601 inputs with offsets, like 2026-02-17T09:45:00-08:00.
  • Store: an absolute timestamp (UTC-based).
  • Display: a localized, clearly labeled time for the user’s zone.

If you’re ever unsure what a schedule means, run a quick conversion using the Timezone Converter.

Wall Clock vs Monotonic Clock (Timeouts Should Not Use “Now”)

Wall clock time can jump because of clock sync corrections (NTP), VM pauses, sleep/wake cycles, or manual changes. If you measure durations using wall time, you can get negative or wildly wrong values.

For timeouts, retries, and performance timing, use a monotonic clock (whatever your platform calls it). Save wall time for “what date/time was it?”

Concrete Examples (JavaScript + Python)

These aren’t “the one true way,” but they show the shape of a safe approach: UTC timestamps as storage, explicit zones for display.

const ts = Date.now();

const utc = new Intl.DateTimeFormat("en-GB", {
  timeZone: "UTC",
  dateStyle: "medium",
  timeStyle: "long"
}).format(new Date(ts));

const la = new Intl.DateTimeFormat("en-US", {
  timeZone: "America/Los_Angeles",
  dateStyle: "medium",
  timeStyle: "short"
}).format(new Date(ts));
from datetime import datetime, timezone

ts = datetime.now(timezone.utc).isoformat()

If you need to coordinate humans across zones, the workflow usually looks like: pick a reference instant (often UTC), then convert and communicate clearly. The Meeting Planner is built for that.

How Teams Avoid Time-Related Incidents

If time mistakes are showing up as team conflict (“you missed the deadline” vs “it wasn’t Friday for me”), read Time Zones and Global Business Conflicts.

FAQ

Is it okay to store local time in the database?

Sometimes, but only when “local wall time” is truly the business concept (like store opening hours). For instants (payments, events, logins), store UTC and convert for display.

What should I do about leap seconds?

Most product teams don’t need to manage leap seconds directly, but they should understand that not every minute behaves perfectly in all systems. If your system needs high precision, use the platform’s recommended approach and test carefully.

Why do reports disagree between regions?

Because “a day” depends on the time zone. If one system groups by UTC and another groups by local time, totals can differ around midnight boundaries. Decide a reporting standard and document it.

Conclusion

Developers handle time by treating it as multiple different data types, not one. UTC keeps instants consistent. IANA time zones keep local displays correct. DST edge cases get explicit rules. Durations use monotonic clocks.

It’s not glamorous, but it’s what prevents the kind of bug that only shows up on a Sunday at 2 AM in one country and crashes a global launch.

Convert and Coordinate Like a Pro

Pick an instant, then translate it for every region.