Dates: /photos date_from/date_to filters on the true-instant contract #152

Closed
opened 2026-09-30 00:41:33 +00:00 by cameron · 1 comment
Owner

Part of #113

Problem

/photos date_from / date_to filters still run on the legacy mixed-convention date_taken; #119 deferred them "to #120/follow-up" but no ticket exists. After the #120 cutover this is the last surface where a date query disagrees with what the UI shows (e.g. mobile search date-range filtering, and the unified-search translation that emits date_from/date_to): a photo can display inside a range yet be filtered out of it, by up to the photo's UTC offset.

What to build

Filter on the true instant (date_utc, with row_true_instant's per-source fallback for the unconverted floor), with range bounds meaning the owner's display-zone days — the same grouping rule as /memories (row_display_day). Clients then send calendar days (or instants built in the display zone) and never shift.

Acceptance

  • A travel-zone photo (e.g. Tokyo 23:30 wall) is included/excluded by a range exactly as its owner-day grouping says
  • Unconverted floor rows filter correctly via the fallback
  • dev/e2e gains fixtures/assertions for range edges; e2e.sh check-style verification
  • Client date-range pickers (FileViewer PhotosBottomSheet, unified search chips) documented against the new semantics

Scope: /photos/exif too (added 2026-10-10)

The legacy-column filter lives in the shared ExifDao::query_by_exif (date_taken.ge/le + is_not_null), so it isn't only /photos. All three callers inherit it:

  • /photos (files::list_photos)
  • /photos/exif (files::list_exif_summary): Apollo's photo-to-track matching feed. Apollo builds its time windows from real-UTC track instants, so today edge photos can drop out of a window by up to their UTC offset.
  • unified search (unified_search.rs)

Fix it once in the DAO, not per handler. After this lands, Apollo #37 can delete its window re-anchoring (photo.py _naive_to_real_utc on the query side, chat_tools._real_utc_to_naive_unix).

Related, already done on branch feature/exif-summary-instant-fields (not merged): /photos/exif rows now carry true_instant + wall_clock (date_view::row_true_instant / row_wall_clock), so the unconverted floor has an instant without client-side fallback logic. That covers the response side; this ticket is the filter side.

Additional acceptance

  • /photos/exif and unified search filter on the true instant, same as /photos (one DAO change)
  • A /photos/exif range built from real-UTC instants includes a photo whose true_instant falls inside it, regardless of its legacy date_taken

Parent: #113 / #120.

🤖 Generated with Claude Code

Part of #113 ## Problem `/photos` `date_from` / `date_to` filters still run on the legacy mixed-convention `date_taken`; #119 deferred them "to #120/follow-up" but no ticket exists. After the #120 cutover this is the last surface where a date query disagrees with what the UI shows (e.g. mobile search date-range filtering, and the unified-search translation that emits `date_from`/`date_to`): a photo can display inside a range yet be filtered out of it, by up to the photo's UTC offset. ## What to build Filter on the **true instant** (`date_utc`, with `row_true_instant`'s per-source fallback for the unconverted floor), with range bounds meaning the owner's **display-zone** days — the same grouping rule as `/memories` (`row_display_day`). Clients then send calendar days (or instants built in the display zone) and never shift. ## Acceptance - [ ] A travel-zone photo (e.g. Tokyo 23:30 wall) is included/excluded by a range exactly as its owner-day grouping says - [ ] Unconverted floor rows filter correctly via the fallback - [ ] `dev/e2e` gains fixtures/assertions for range edges; `e2e.sh check`-style verification - [ ] Client date-range pickers (FileViewer `PhotosBottomSheet`, unified search chips) documented against the new semantics ## Scope: `/photos/exif` too (added 2026-10-10) The legacy-column filter lives in the shared `ExifDao::query_by_exif` (`date_taken.ge/le` + `is_not_null`), so it isn't only `/photos`. All three callers inherit it: - `/photos` (`files::list_photos`) - **`/photos/exif`** (`files::list_exif_summary`): Apollo's photo-to-track matching feed. Apollo builds its time windows from real-UTC track instants, so today edge photos can drop out of a window by up to their UTC offset. - unified search (`unified_search.rs`) Fix it once in the DAO, not per handler. After this lands, Apollo #37 can delete its window re-anchoring (`photo.py` `_naive_to_real_utc` on the query side, `chat_tools._real_utc_to_naive_unix`). Related, already done on branch `feature/exif-summary-instant-fields` (not merged): `/photos/exif` rows now carry `true_instant` + `wall_clock` (`date_view::row_true_instant` / `row_wall_clock`), so the unconverted floor has an instant without client-side fallback logic. That covers the response side; this ticket is the filter side. ### Additional acceptance - [ ] `/photos/exif` and unified search filter on the true instant, same as `/photos` (one DAO change) - [ ] A `/photos/exif` range built from real-UTC instants includes a photo whose `true_instant` falls inside it, regardless of its legacy `date_taken` Parent: #113 / #120. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
cameron added the needs-triage label 2026-09-30 00:42:48 +00:00
cameron added wayfinder:task and removed needs-triage labels 2026-09-30 00:51:51 +00:00
Author
Owner

Done. Server-side true-instant filtering landed in #162. The last checklist item (client date-range semantics) is covered by #178, which adds date_from_day / date_to_day (YYYY-MM-DD), turned into display-zone day bounds by date_view::apply_day_bounds, plus FileViewer-React #47, where search sends calendar days instead of device-local midnights. The messages photo sheet keeps true-instant ±3-day bounds on purpose. e2e check now also asserts display-zone day edges for every fixture (97/97).

Done. Server-side true-instant filtering landed in #162. The last checklist item (client date-range semantics) is covered by #178, which adds `date_from_day` / `date_to_day` (YYYY-MM-DD), turned into display-zone day bounds by `date_view::apply_day_bounds`, plus FileViewer-React #47, where search sends calendar days instead of device-local midnights. The messages photo sheet keeps true-instant ±3-day bounds on purpose. e2e `check` now also asserts display-zone day edges for every fixture (97/97).
Sign in to join this conversation.