fix: SQLite timestamp timezone ambiguity — convert to proper UTC ISO strings

This commit is contained in:
null 2026-06-04 21:42:34 -05:00
parent df9d6fbf6d
commit 3a19303d4d
1 changed files with 16 additions and 2 deletions

View File

@ -18,6 +18,20 @@ function errorMessage(err) {
return err?.message || String(err);
}
// SQLite stores datetime('now') as "YYYY-MM-DD HH:MM:SS" (UTC, no Z).
// Without the Z suffix, JS Date parses it as LOCAL time, creating timezone
// inconsistencies when mixed with ISO strings that do have Z.
// This ensures all timestamps sent to clients are unambiguous UTC ISO strings.
function toIso(sqliteOrIso) {
if (!sqliteOrIso) return null;
const s = String(sqliteOrIso).trim();
// Already ISO with Z or offset — return as-is
if (s.includes('T') && (s.endsWith('Z') || s.match(/[+-]\d{2}:\d{2}$/))) return s;
// SQLite format "YYYY-MM-DD HH:MM:SS" — append Z to declare it UTC
if (/^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}/.test(s)) return s.replace(' ', 'T') + 'Z';
return s;
}
function monthRange(now) {
const year = now.getFullYear();
const month = now.getMonth() + 1;
@ -291,7 +305,7 @@ router.get('/', async (req, res) => {
source_count: sourceRow.source_count ?? 0,
account_count: accountRow.account_count ?? 0,
transaction_count: txRow.transaction_count ?? 0,
last_sync_at: sourceRow.last_sync_at || null,
last_sync_at: toIso(sourceRow.last_sync_at),
last_error: errorRow?.last_error || null,
};
}
@ -305,7 +319,7 @@ router.get('/', async (req, res) => {
const raw = getSetting('cleanup_last_result');
cleanup = {
ok: true,
last_run_at: getSetting('cleanup_last_run_at') || null,
last_run_at: toIso(getSetting('cleanup_last_run_at')),
last_result: raw ? JSON.parse(raw) : null,
};
} catch { /* non-fatal */ }