fix(desktop): prevent double-localized SQL timestamps - #12351
fix(desktop): prevent double-localized SQL timestamps#12351tianmind-studio wants to merge 1 commit into
Conversation
Failure-Class: FC-naive-utc-timestamp-in-llm-surface
b62c275 to
edbff15
Compare
Git-on-my-level
left a comment
There was a problem hiding this comment.
Approved — clean, focused fix for the double-localization regression from #12349 (#12350), and it matches the merged design: the result formatter owns localization, so the query layer stays UTC.
Per-file notes:
desktop/macos/Desktop/Sources/Chat/ChatPrompts.swift— examples now select rawtimestamp/*Atcolumns (MIN(timestamp) AS firstSeenAt, MAX(timestamp) AS lastSeenAt) and express local-day bounds as UTC instants (datetime('now', 'localtime', 'start of day', '-1 day', 'utc')). I verified the modifier order empirically against SQLite in Asia/Shanghai: the old form resolves yesterday's lower bound to 08:00 UTC while the new form gives 16:00 UTC — true local midnight. The old "convert them to {tz}" display guidance is correctly replaced by "quote execute_sql output as-is".desktop/macos/Desktop/Sources/Chat/SQLQueryResultProjection.swift— the newprojectsSQLiteLocalTimeguard returns an actionable error instead of letting adatetime(col, 'localtime')projection reach the formatter that would shift it a second time;localtimeinside UTC WHERE-boundary computation is still allowed (covered by the new test). Minor non-blocking corner case: a projection likeSELECT datetime('now','localtime','start of day','utc') AS today_startalso trips the guard even though that value is already UTC — it degrades to a clear error message, fine to leave as-is for a conservative guard.desktop/macos/Desktop/Sources/Providers/ChatToolExecutor.swift—timeZoneis threaded explicitly throughexecuteSQL→executeSelectQuery→ the projection (default.currentkeeps existing call sites unchanged), andDesktopChatSQLTime.localDayStartAsUTC/exclusiveEndAsUTCreplace the six hand-rolledlocaltimebounds in the daily recap queries. The recap'sfirst_seen/last_seenstay SQL-localized and now carry an explicit zone label, so both paths localize exactly once.desktop/macos/Desktop/Tests/ChatPromptsTests.swift— pins the new guidance (raw projections, UTC-bound comparisons) and theDesktopChatSQLTimeboundary strings, including the daysAgo=0 and negative-days clamping cases.desktop/macos/Desktop/Tests/ChatToolExecutorSQLTests.swift— the pinnedAmerica/New_Yorktimezone makes rendering deterministic on any CI machine, and the new test exercises both the allowed boundary form and the rejected projection form through the realexecuteSQLpath. Swapping the magicDate(timeIntervalSince1970:)forISO8601DateFormatterparsing is a readability win.desktop/macos/changelog/unreleased/20260828-chat-sql-timezone-boundaries.json— accurate one-liner matching the unreleased-changelog convention.
Coordination note for maintainers: #12326 (SCA-364) is also open and touches the same five Swift files with the same UTC-boundary idiom, though it does not include this projection guard. Sequencing the two is a maintainer call; this PR is the focused fix for the live regression and is mergeable as-is against current main.
Thanks for the fast, well-reproduced report in #12350 and the tight follow-up.
by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.
What changed and why
Fixes #12350. After #12349, Desktop Chat could apply the local offset twice when a SQL projection used SQLite
localtime, while the prompt and daily recap still built local-day bounds in UTC-first order. This focused follow-up to the still-open macOS work in #12326 keeps projected timestamps raw until the result formatter, rejects query-layerlocaltimeinSELECTexpressions, and converts local-midnight boundaries back to UTC before comparing stored UTC values.Product invariants affected
How it was verified
Asia/Shanghai: the old boundary resolves to 08:00 local, whilelocaltime -> start of day -> utcresolves to 16:00 UTC (local midnight).git diff --checklocaltimein UTC boundary predicates and rejection in projected timestamp expressions.Desktop Swift Static & Test Contractsis the authoritative compile/test gate.Tests
ChatPromptsTests.testDesktopChatSQLGuidanceLocalizesTimestampResultsExactlyOnceChatPromptsTests.testDesktopChatSQLGuidanceComparesUTCColumnsToUTCBoundsChatPromptsTests.testDesktopChatSQLDayBoundsKeepLocalCalendarWindowsInUTCChatToolExecutorSQLTests.testExecuteSQLRendersDatetimeColumnsInLocalTimeWithZoneLabelChatToolExecutorSQLTests.testExecuteSQLRejectsProjectedLocaltimeButAllowsUTCBoundaryConversionFailure class (fixes)
Failure-Class: FC-naive-utc-timestamp-in-llm-surface