fix(backend): drop AT TIME ZONE 'UTC' from draft.created_at migration

The original migration forced `USING created_at AT TIME ZONE 'UTC'`,
which tags every existing wall-clock value as UTC. That matches the
common case (Postgres on a UTC server, which the Docker image and most
managed offerings default to), but on a non-UTC operator's deployment
it shifts all pre-migration timestamps by the server's tz offset.

Drop the USING clause. Postgres's default `TIMESTAMP -> TIMESTAMPTZ`
cast reinterprets each existing value in the session's current
timezone — which is the same timezone under which the original
`INSERT ... DEFAULT now()` values were truncated to TIMESTAMP, so
the conversion correctly recovers the original instant regardless of
the operator's timezone. Same semantics on UTC servers, correct
semantics on non-UTC servers.

Down migration updated symmetrically.
This commit is contained in:
Diego Imbert
2026-05-15 02:56:38 +02:00
parent 30a0eeda61
commit 397256af1e
2 changed files with 15 additions and 7 deletions
@@ -1,2 +1,5 @@
ALTER TABLE draft
ALTER COLUMN created_at TYPE TIMESTAMP USING created_at AT TIME ZONE 'UTC';
-- Symmetric to the up migration: Postgres's default `TIMESTAMPTZ -> TIMESTAMP`
-- cast strips the timezone by representing the instant in the session's
-- current timezone, mirroring how the original `now()` values were
-- truncated on insert.
ALTER TABLE draft ALTER COLUMN created_at TYPE TIMESTAMP;
@@ -1,7 +1,12 @@
-- `draft.created_at` was originally created as `TIMESTAMP` (no timezone). The
-- new `*WithDraft` API responses surface it as `chrono::DateTime<Utc>` for the
-- frontend's staleness check, which requires `TIMESTAMPTZ`. Existing values
-- are interpreted as UTC (matching `now()`'s behaviour on a UTC server, the
-- expected deployment for Windmill).
ALTER TABLE draft
ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'UTC';
-- frontend's staleness check, which requires `TIMESTAMPTZ`.
--
-- We rely on Postgres's default `TIMESTAMP -> TIMESTAMPTZ` cast (no explicit
-- USING), which interprets each existing wall-clock value in the session's
-- current timezone. That's the exact semantics under which the original
-- `INSERT ... DEFAULT now()` values were truncated to TIMESTAMP — so the
-- conversion is a no-op on UTC servers (the common case) and correctly
-- recovers the original instant on non-UTC servers, instead of shifting all
-- pre-migration timestamps by the server's tz offset.
ALTER TABLE draft ALTER COLUMN created_at TYPE TIMESTAMPTZ;