Postgres Timestamp Vs Timestamptz ((install)) -
Now, what is stored?
Chances are, you chose the wrong PostgreSQL temporal data type.
-- Assume my session time zone is 'America/New_York' SET TIME ZONE 'America/New_York'; -- Create a test table CREATE TABLE time_test ( ts_native TIMESTAMP, -- without tz ts_tz TIMESTAMPTZ -- with tz ); postgres timestamp vs timestamptz
CREATE TABLE events ( id SERIAL, local_start TIMESTAMPTZ, -- absolute moment in UTC user_time_zone TEXT -- 'America/Los_Angeles' ); | Feature | TIMESTAMP | TIMESTAMPTZ | |---------|-------------|----------------| | Time zone awareness | ❌ No | ✅ Yes (UTC internally) | | Changes with client time zone | ❌ No | ✅ Yes (on output) | | Safe for global apps | ❌ Risky | ✅ Safe | | Storage size | 8 bytes | 8 bytes (same!) |
To preserve the user's original time zone (e.g., for compliance or display), you need a : Now, what is stored
| Column | What PostgreSQL stores internally | |--------|----------------------------------| | ts_native | 2025-04-14 14:00:00 (exact text, no zone info) | | ts_tz | A UTC timestamp: 2025-04-14 18:00:00+00 (because 2pm ET = 6pm UTC) |
If you have ever built an app that serves users across multiple time zones, you’ve likely woken up to a 3:00 AM page about "incorrect order dates" or "meetings showing up at the wrong hour." If your server is in UTC and your user is in Sydney –
If your column is TIMESTAMPTZ , but your application sends a naive timestamp, PostgreSQL will assume the timestamp is in your session's time zone. If your server is in UTC and your user is in Sydney – .