fix: Use timezone-aware timestamps for migration state tracking

Fix timezone inconsistency between migration_started_at and migration_completed_at:

Schema Changes:
- Change TIMESTAMP to TIMESTAMPTZ for migration_started_at and migration_completed_at
- PostgreSQL stores timestamps in UTC and converts to local timezone on display
- Ensures consistent timezone handling across all timestamp columns

Code Changes:
- Replace datetime.utcnow() with datetime.now(timezone.utc)
- Use timezone-aware datetime objects for proper TIMESTAMPTZ handling
- Import timezone module for UTC timezone support

Impact:
- Previous issue: migration_completed_at was 1 hour behind migration_started_at
- Root cause: CURRENT_TIMESTAMP (local time) vs datetime.utcnow() (UTC naive)
- Solution: Both columns now use TIMESTAMPTZ with timezone-aware datetimes

Note: Existing migration_state records will have old TIMESTAMP format until table is altered:
  ALTER TABLE migration_state
    ALTER COLUMN migration_started_at TYPE TIMESTAMPTZ,
    ALTER COLUMN migration_completed_at TYPE TIMESTAMPTZ;

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-04 16:04:15 +01:00
parent 23e9fc9d82
commit a7d2d501fb
2 changed files with 4 additions and 4 deletions

View File

@@ -7,7 +7,7 @@ Tracks migration progress with:
- status: pending, in_progress, completed - status: pending, in_progress, completed
""" """
from typing import Optional, Dict, Any from typing import Optional, Dict, Any
from datetime import datetime from datetime import datetime, timezone
import json import json
from src.connectors.postgres_connector import PostgreSQLConnector from src.connectors.postgres_connector import PostgreSQLConnector
@@ -199,7 +199,7 @@ class StateManager:
if mark_completed: if mark_completed:
updates.append("migration_completed_at = %s") updates.append("migration_completed_at = %s")
params.append(datetime.utcnow()) params.append(datetime.now(timezone.utc))
if status is None: if status is None:
updates.append("status = 'completed'") updates.append("status = 'completed'")

View File

@@ -183,8 +183,8 @@ CREATE TABLE IF NOT EXISTS migration_state (
table_name VARCHAR(255) NOT NULL, table_name VARCHAR(255) NOT NULL,
partition_name VARCHAR(255) NOT NULL, partition_name VARCHAR(255) NOT NULL,
last_key JSONB, last_key JSONB,
migration_started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, migration_started_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
migration_completed_at TIMESTAMP, migration_completed_at TIMESTAMPTZ,
total_rows_migrated BIGINT DEFAULT 0, total_rows_migrated BIGINT DEFAULT 0,
status VARCHAR(32) DEFAULT 'pending', status VARCHAR(32) DEFAULT 'pending',
PRIMARY KEY (table_name, partition_name), PRIMARY KEY (table_name, partition_name),