Timestamps stored without a time zone
What it looks like
create table bookings (
id uuid primary key,
starts_at timestamp, -- no time zone
created_at timestamp default now()
);
// browser: starts_at: `${form.date} ${form.time}` // local wall-clock time, no offsetWhat happens
Appointments, deadlines and reports drift by hours depending on where the user and the server sit, and the drift changes again when daylight saving starts or ends.
Where it shows up
Descriptive, not a verdict: builders and stacks whose defaults make this reachable, or that a public incident names. Each hub states the tool's own security features.
Check it yourself
Uses your builder's dashboard and your browser. No tools to install.
- In a test account, create a booking or event for a specific clock time tomorrow and note it.
- Change your computer's time zone in its system settings (or open the app on a phone set to another zone) and reload the app.
- Open the row in the Supabase Table Editor and compare the stored value with what each device shows.
- Bad result: the time shifts between devices by the zone difference, or the stored value carries no offset and does not match the moment you entered.
What we check
We list every date and time column in the schema and its type, trace how the app writes and displays each, and test entries on both sides of a daylight-saving change. The report names the fields that drift and by how much.
Fix direction
Use timestamptz (timestamp with time zone, which Postgres stores as UTC and converts on display) for every point in time, and send values from the browser with an explicit offset. Keep a plain date type only for calendar dates with no clock, such as a birthday. When converting existing columns, state the zone you assume the old values were in and record that assumption.
Mechanism documented in: Date/Time Types (verified 2026-09-21).
Related patterns
- Migrations that cannot be undoneData integrityMedium
When the change turns out to be wrong, the old column values and deleted rows are already gone, and the only way back is a full restore from backup, if one exists.
- Offline state corrupting dataMobileHigh
Users lose edits they watched themselves save, records end up half-updated, and nobody can tell which copy of the data is the real one.
- Background jobs that fail silentlyReliabilityMedium
Emails, invoices, syncs or clean-ups quietly stop for some or all users, and you find out weeks later from a customer rather than from the system.
- Deletes with no soft delete or audit trailData integrityHigh
One wrong click, a bug in a filter, or an abused account removes customer data for good, and nobody can say afterwards what was lost or who removed it.
Questions
should i use timestamp or timestamptz in postgres
Use timestamptz for anything that is a moment in time. Postgres documents that a plain timestamp ignores any zone in the input and stores the digits as written. timestamptz converts to UTC on the way in and to the session's zone on the way out. The plain type is only right for values that are deliberately zone-free.
why are my supabase times off by a few hours
The usual cause is a timestamp column without time zone receiving a browser's local time, then being read back as if it were UTC or the server's zone. Check the column type in the Table Editor, then compare one stored value with what the app shows in two different zones. If they differ by a fixed number of hours, the type is the problem.
how to store time zones in a booking app
Store the moment as timestamptz and, separately, store the customer's zone name (such as Europe/Berlin) as text so you can display and send reminders in their local time. Never store a local wall-clock time on its own for something that has to happen at a real moment. Test around a daylight-saving change, where most drift appears.
Back to the catalogue, or run the self-check for your stack.