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

+12
View File
@@ -77,6 +77,10 @@ pub struct UnifiedQuery {
pub media_type: Option<MediaType>,
pub date_from: Option<i64>,
pub date_to: Option<i64>,
/// Calendar days (`YYYY-MM-DD`) in the owner's display zone; each
/// replaces its instant counterpart (see `date_view::apply_day_bounds`).
pub date_from_day: Option<String>,
pub date_to_day: Option<String>,
pub camera_make: Option<String>,
pub camera_model: Option<String>,
pub gps_lat: Option<f64>,
@@ -292,6 +296,14 @@ pub async fn unified_search<TagD: TagDao>(
tag_dao: Data<Mutex<TagD>>,
query: Query<UnifiedQuery>,
) -> HttpResponse {
let mut query = query.into_inner();
crate::date_view::apply_day_bounds(
&mut query.date_from,
&mut query.date_to,
query.date_from_day.as_deref(),
query.date_to_day.as_deref(),
|| crate::date_view::owner_display_zone(&state.user_ai_prefs_dao),
);
let nl = query.q.trim().to_string();
if nl.is_empty() {
return bad_request("query parameter `q` is required");