feat(dates): calendar-day date filters in the owner's display zone (#152)

/photos, /photos/exif and unified search take date_from_day /
date_to_day (YYYY-MM-DD): the server turns them into the first day's
start and the last day's end in the owner's display zone, the same days
/memories groups by. Each replaces its instant counterpart. Clients stop
computing midnights in the device's zone, which drifted by the travel
offset whenever the phone's zone differed from display_tz.

e2e check: every fixture is included by its display-zone day and
excluded by the days either side.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Cameron CordesandClaude Opus 5.5 authored and cameron committed 2026-10-17 03:24:50 +00:00
1 parent a11d20bdcd
commit bfc3c1c21a
5 files changed
+133 -5

No files matched your search

+18 -1
View File
@@ -199,6 +199,14 @@ pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
tag_dao: Data<Mutex<TagD>>,
exif_dao: Data<Mutex<Box<dyn ExifDao>>>,
) -> HttpResponse {
let mut req = req.into_inner();
crate::date_view::apply_day_bounds(
&mut req.date_from,
&mut req.date_to,
req.date_from_day.as_deref(),
req.date_to_day.as_deref(),
|| crate::date_view::owner_display_zone(&app_state.user_ai_prefs_dao),
);
let search_path = &req.path;
let span = tracing::Span::current();
@@ -1176,7 +1184,8 @@ pub async fn get_gps_summary(
/// Handler for the batch EXIF endpoint at `GET /photos/exif`.
///
/// Returns a single-row projection of `image_exif` for every photo whose
/// `date_taken` falls in `[date_from, date_to]`, across all libraries.
/// true instant falls in `[date_from, date_to]` (or the display-zone
/// `date_from_day` / `date_to_day`), across all libraries.
/// Designed to replace the N+1 pattern of `/photos` + per-file
/// `/image/metadata` for window-scoped consumers like Apollo's photo-to-
/// track matcher: one DB query, one HTTP round-trip, one mutex acquisition.
@@ -1194,6 +1203,14 @@ pub async fn list_exif_summary(
app_state: Data<AppState>,
) -> Result<HttpResponse, actix_web::Error> {
let span = tracing::Span::current();
let mut req = req.into_inner();
crate::date_view::apply_day_bounds(
&mut req.date_from,
&mut req.date_to,
req.date_from_day.as_deref(),
req.date_to_day.as_deref(),
|| crate::date_view::owner_display_zone(&app_state.user_ai_prefs_dao),
);
span.set_attribute(
"date_from",