Skip to content

Fix: Add timezone offset to Immich memories "for" query parameter#660

Merged
JW-CH merged 1 commit into
mainfrom
memories_fix
Jul 7, 2026
Merged

Fix: Add timezone offset to Immich memories "for" query parameter#660
JW-CH merged 1 commit into
mainfrom
memories_fix

Conversation

@JW-CH

@JW-CH JW-CH commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Closes #659

Summary by CodeRabbit

  • Bug Fixes
    • Fixed date-based requests so timezone offsets are preserved, improving accuracy for time-sensitive results across different locales.
    • Prevented query values from being altered in ways that could cause unexpected filtering or missed items.

@JW-CH JW-CH added the fix Something was fixed label Jul 3, 2026
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a PrepareRequest hook and EnsureTimezoneOffset helper to ImmichApi that re-appends a timezone offset to the for query parameter before sending requests, since NSwag strips it during serialization. MemoryAssetsPool gains an explanatory comment referencing this fix.

Changes

Timezone Offset Restoration

Layer / File(s) Summary
PrepareRequest and EnsureTimezoneOffset
ImmichFrame.Core/Api/ImmichApi.cs, ImmichFrame.Core/Logic/Pool/MemoryAssetsPool.cs
Adds PrepareRequest partial method calling new EnsureTimezoneOffset, which finds the for= query parameter, parses it as DateTimeOffset (assuming local), and rewrites it with a zzz timezone offset format in the request URL; adds an explanatory comment in MemoryAssetsPool.LoadAssets.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MemoryAssetsPool
  participant ImmichApi
  participant ImmichServer

  MemoryAssetsPool->>ImmichApi: SearchMemoriesAsync(for)
  ImmichApi->>ImmichApi: PrepareRequest(url)
  ImmichApi->>ImmichApi: EnsureTimezoneOffset(url)
  ImmichApi->>ImmichServer: GET /memories?for=...zzz
  ImmichServer-->>ImmichApi: 200 OK
  ImmichApi-->>MemoryAssetsPool: memories result
Loading

Possibly related PRs

Suggested labels: maintenance

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly states the main fix: adding a timezone offset to the Immich memories for parameter.
Linked Issues check ✅ Passed The change addresses #659 by rewriting the for query to preserve timezone information so Immich accepts the request.
Out of Scope Changes check ✅ Passed The PR stays focused on the memories request fix and adds only a clarifying comment without unrelated code changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch memories_fix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
ImmichFrame.Core/Api/ImmichApi.cs (1)

58-70: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Manual query-string index math is fragile; consider HttpUtility.ParseQueryString.

Locating and replacing the parameter value via raw IndexOf/Substring on the URL string works but is easy to get subtly wrong on future edits (e.g., if NSwag ever double-encodes or reorders parameters). Using System.Web.HttpUtility.ParseQueryString (or splitting on ?/& more defensively) would reduce that risk.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ImmichFrame.Core/Api/ImmichApi.cs` around lines 58 - 70, The query-parameter
extraction logic in ImmichApi is using fragile manual IndexOf/Substring parsing,
which can break with encoding or parameter ordering changes. Update the URL
handling in the relevant parameter-replacement helper to parse the query string
more defensively, preferably via HttpUtility.ParseQueryString, and use the
parsed collection to locate and replace the target parameter value instead of
relying on raw string index math.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ImmichFrame.Core/Api/ImmichApi.cs`:
- Around line 56-87: Add direct unit tests for EnsureTimezoneOffset in ImmichApi
to cover its branching behavior: missing parameter, missing query delimiter,
unparsable value, and unchanged value when the offset is already correct. Use
the method name EnsureTimezoneOffset as the target and assert the resulting
query string after rewriting so the tests validate the exact URL mutation rather
than only downstream LoadAssets behavior.

---

Nitpick comments:
In `@ImmichFrame.Core/Api/ImmichApi.cs`:
- Around line 58-70: The query-parameter extraction logic in ImmichApi is using
fragile manual IndexOf/Substring parsing, which can break with encoding or
parameter ordering changes. Update the URL handling in the relevant
parameter-replacement helper to parse the query string more defensively,
preferably via HttpUtility.ParseQueryString, and use the parsed collection to
locate and replace the target parameter value instead of relying on raw string
index math.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bf358a05-9553-434e-9e96-c771b017d55a

📥 Commits

Reviewing files that changed from the base of the PR and between 682fdc4 and cc947a4.

📒 Files selected for processing (2)
  • ImmichFrame.Core/Api/ImmichApi.cs
  • ImmichFrame.Core/Logic/Pool/MemoryAssetsPool.cs

Comment on lines +56 to +87
private static void EnsureTimezoneOffset(System.Text.StringBuilder urlBuilder, string parameterName)
{
var url = urlBuilder.ToString();
var token = parameterName + "=";

var idx = url.IndexOf("?" + token, StringComparison.Ordinal);
if (idx < 0) idx = url.IndexOf("&" + token, StringComparison.Ordinal);
if (idx < 0) return;

var valueStart = idx + 1 + token.Length;
var valueEnd = url.IndexOf('&', valueStart);
if (valueEnd < 0) valueEnd = url.Length;

var encodedValue = url.Substring(valueStart, valueEnd - valueStart);
var rawValue = Uri.UnescapeDataString(encodedValue);

// Only rewrite values that look like a datetime; leave anything else untouched.
// The original offset is already lost at this point (stripped by "s"), so we re-infer it
// with AssumeLocal, i.e. the machine's local offset. This is correct for the only caller
// today (SearchMemoriesAsync with DateTimeOffset.Now). If a caller ever passes a value
// with a different offset (e.g. UTC), it would be normalized to the local offset here.
if (!DateTimeOffset.TryParse(rawValue, System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.AssumeLocal, out var value))
return;

var fixedValue = Uri.EscapeDataString(
value.ToString("yyyy-MM-ddTHH:mm:sszzz", System.Globalization.CultureInfo.InvariantCulture));
if (fixedValue == encodedValue) return;

urlBuilder.Remove(valueStart, valueEnd - valueStart);
urlBuilder.Insert(valueStart, fixedValue);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add unit tests for EnsureTimezoneOffset.

This method fixes a production-breaking 400 error and contains several branches (missing parameter, missing delimiter, unparsable value, unchanged value). Direct unit tests covering these cases (e.g., asserting the rewritten query string) would give higher confidence than relying solely on downstream LoadAssets tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ImmichFrame.Core/Api/ImmichApi.cs` around lines 56 - 87, Add direct unit
tests for EnsureTimezoneOffset in ImmichApi to cover its branching behavior:
missing parameter, missing query delimiter, unparsable value, and unchanged
value when the offset is already correct. Use the method name
EnsureTimezoneOffset as the target and assert the resulting query string after
rewriting so the tests validate the exact URL mutation rather than only
downstream LoadAssets behavior.

@JW-CH JW-CH merged commit eb5674d into main Jul 7, 2026
8 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix Something was fixed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Immich V3 and ShowMemories=true breaks Frame

1 participant