Timestamp Without Time Zone
Storing instants without zone information makes timestamps ambiguous across regions and DST shifts, corrupting ordering, scheduling, and reporting. Store UTC in a zone-aware type, transmit ISO 8601 with offsets, and convert to local only at display time.
This anti-pattern stores a point in time without recording its time zone — using a naive local-time value or a database type that explicitly carries no zone — so the stored number does not unambiguously identify an instant. "2026-06-27 14:00" means different moments in New York, London, and Tokyo, and the database has no way to tell them apart.
Why It Happens
Servers and developers historically lived in one time zone, so local time seemed sufficient. The default timestamp type in some databases carries no zone, and it gets used without a second thought. Time-zone handling is genuinely subtle, so it is easy to defer until "later," which never comes. Data from multiple regions then accumulates in the same column with no zone to disambiguate it.
Why It Hurts
Without a zone, a stored timestamp is ambiguous. Comparing or ordering events recorded in different zones produces wrong results. Daylight-saving transitions make some local times occur twice and others not at all, so naive local storage cannot represent them correctly, yielding classic off-by-an-hour bugs and duplicated or missing events. When servers move regions or run in UTC while clients are local, conversions silently shift every value. Reporting, scheduling, and audit trails become unreliable, and the errors are easy to overlook because most timestamps still look plausible.
Warning Signs
- Columns use a timestamp-without-time-zone type for real instants.
- Stored times are in server or user local time with no zone recorded.
- Recurring off-by-an-hour or DST-related defects in scheduling or ordering.
- Cross-region data lands in one column with no way to reconcile the zones.
Better Alternatives
Store instants in UTC using a zone-aware type (such as timestamptz), and convert to a user's local zone only at display time. Encode and transmit times in ISO 8601 with an explicit offset so no consumer has to guess. When the original local zone is itself meaningful — for example a calendar appointment in a specific city — store the UTC instant plus an explicit time-zone column so you can reconstruct local meaning across DST changes.
How to Refactor Out of It
Decide, for each timestamp column, whether it represents an instant (use UTC) or a local wall-clock time (store UTC plus a zone). Migrate naive columns to zone-aware types, interpreting existing values using the zone they were actually recorded in — this requires knowing that zone, so investigate before converting. Normalize all instants to UTC at write time and convert to local only in the presentation layer. Add tests covering DST boundaries and multi-zone ordering. Standardize ISO 8601 with offsets across your APIs to keep the boundary clean.