#2336·TREK

[BUG] Journey photo picker searches provider photos by UTC day, not the local day — morning photos missing from "This day"

Author: MattSidneyCreated Sep 12, 2026Updated Sep 17, 2026
Labelsfixed in next release

TREK version

4.2.1

Describe the bug

The provider photo search (Immich / Synology Photos) used by the Journey photo picker converts the date-only from/to it receives into a UTC day window. For any user east of UTC, "This day" for entry date D actually searches D 00:00–23:59 UTC, which is a window shifted into the local afternoon/next morning.

Concretely at UTC+10 (Australia/Melbourne, September): the searched window for D is 10:00 local on D → 09:59 local on D+1. Two symmetric symptoms:

  1. Photos taken before 10:00 local on the entry's day are missing from the default "This day" results. The user has to switch to the custom date-range tab and widen the range to find their own morning photos.
  2. Photos taken before 10:00 local on the following day are wrongly included in D's results (they fall inside D's UTC day).

In practice this hits every breakfast/morning photo of a trip, silently — there's no indication the day search is incomplete.

The picker's date group headings have the same issue: photos are grouped by the UTC date of takenAt, so a photo taken 07:32 local appears under the previous day's heading.

Setting TZ on the container does not (and cannot) help — the window is built by string concatenation / UTC parsing, not via the process timezone, and on a multi-user server each user has their own timezone anyway.

Steps to reproduce

  1. Run TREK in a timezone well east of UTC (e.g. Australia/Melbourne, UTC+10) with an Immich provider connected. Browser in the same zone.
  2. In Immich, have a photo taken at e.g. 07:32 local on day D (stored by Immich as D−1 21:32 UTC).
  3. Create a Journey entry dated D, open the entry editor → External photos → the picker's default This day filter.
  4. The 07:32 photo is absent. Widen to a custom range starting D−1 → it appears. Conversely, a photo taken 07:00 local on D+1 does show up under "This day" for D.

Expected behavior

"This day" for entry date D returns the photos taken on calendar day D at the place they were captured (at minimum: in the user's local timezone rather than UTC), and date group headings show the capture-local date. West-of-UTC users have the mirror-image problem (evening photos missing instead of morning ones).

Where it happens (code)

Client sends date-only strings; the picker's "This day" filter passes the entry date as both bounds (client/src/components/Journey/JourneyDetailPageProviderPicker.tsx:104-105):

typescript
if (filter === 'day' && initialDate) {
  searchPhotos(initialDate, initialDate)

The server then pins those dates to UTC for Immich (server/src/nest/memories/immich.service.ts:213-214):

typescript
takenAfter: from ? `${from}T00:00:00.000Z` : undefined,
takenBefore: to ? `${to}T23:59:59.999Z` : undefined,

(pinned as-is by server/tests/unit/nest/immich.service.test.ts:326-327).

Synology has the same window through a different expression — new Date(<date-only>) parses as UTC midnight per the JS spec (server/src/nest/memories/synology.service.ts:583-586):

typescript
params.start_time = Math.floor(new Date(from).getTime() / 1000);
...
params.end_time = Math.floor(new Date(to).getTime() / 1000) + 86400;

Display grouping uses the UTC date too (client/src/pages/journeyDetail/JourneyDetailPage.helpers.ts:65):

typescript
const key = asset.takenAt ? asset.takenAt.slice(0, 10) : '__unknown__'

Affected surfaces: all picker filters (This day / Trip period / Date range) in the Journey entry editor, gallery view and both mobile screens, plus the search_provider_photos MCP tool (memories.mcp.ts, searchProviderPhotos) — every path funnels into the two service methods above. The trip-period filter likewise drops the first trip morning and picks up the morning after the trip ends.

Related but distinct: #2280 / #2291 (merged after 4.2.1) fixed the picker's ordering; the day window and the UTC date grouping are unchanged on dev (immich.service.ts:213-214, JourneyDetailPage.helpers.ts:68 there).

Suggested fix

Three possible anchors for "day D", in descending order of correctness:

  1. The photo's own capture-local date. Immich returns localDateTime on every search result (/search/metadataAssetResponseDto), documented as "the photographer's local time regardless of timezone … used for timeline grouping by local days". Query a padded window (the local-day bounds ±1 day) and keep assets whose localDateTime date equals the requested day; group headings use the same field. No timezone lookup needed, entries without a location work, and travel days spanning timezones resolve per-photo — matching what the user sees in Immich's own timeline. The server already post-filters this endpoint (hidden assets) with hasMore on the raw page length, so the pagination pattern exists.
  2. The entry's location timezone, when the entry has coordinates (the picker already receives them for distance sorting). Needs a lat/lng→IANA lookup dependency and a fallback for unlocated entries and the trip/custom/all filters.
  3. The user's browser timezone — the minimal fix and the natural fallback for providers without per-asset local time (Synology): client sends local-day instants (e.g. new Date(from + 'T00:00:00').toISOString()), server passes through values that already contain a time component, keeping the bare-date suffixing for backward compatibility.

Whichever anchor is chosen, the date group headings should switch off takenAt.slice(0, 10) (UTC) to the same anchor.

Deployment method

Docker Compose

Host OS

Ubuntu Server 24.04

Accessing TREK from

Desktop browser

Browser

(any — the window is built server-side; confirmed in Chromium and the PWA)