fix(photos): coerce tz-aware taken_at to naive UTC before DB write
PATCH /api/v1/photos/{id} returned 500 with
'can't subtract offset-naive and offset-aware datetimes' when the
frontend sent a tz-aware taken_at value (e.g. 2026-05-09T00:12+02:00).
The photos.taken_at column is timestamp without time zone, so asyncpg
refuses to bind a tz-aware datetime.
The frontend's datetime-local input is supposed to be naive but real-
world locales / browsers / paste flows occasionally include offsets.
Normalize on the server: if tzinfo is present, convert to UTC and drop
the tzinfo so both shapes round-trip cleanly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
Photos API router
|
||||
"""
|
||||
from typing import List, Optional, Dict, Any
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Response
|
||||
from fastapi.responses import FileResponse
|
||||
@@ -888,6 +888,15 @@ async def update_photo(
|
||||
if 'taken_at' in update_data:
|
||||
new_dt = update_data.pop('taken_at')
|
||||
if new_dt is not None:
|
||||
# The frontend's <input type="datetime-local"> usually serializes
|
||||
# without a tz, but a manual edit / paste / certain locales can
|
||||
# send a tz-aware ISO (e.g. "2026-05-09T00:12+02:00"). The
|
||||
# photos.taken_at column is `timestamp without time zone`, so
|
||||
# asyncpg can't bind a tz-aware value — it raises
|
||||
# "can't subtract offset-naive and offset-aware datetimes".
|
||||
# Normalize to naive UTC so both shapes round-trip cleanly.
|
||||
if new_dt.tzinfo is not None:
|
||||
new_dt = new_dt.astimezone(timezone.utc).replace(tzinfo=None)
|
||||
try:
|
||||
await write_taken_at(photo.filepath, new_dt)
|
||||
except ExifWriteError as exc:
|
||||
|
||||
Reference in New Issue
Block a user