viewers: render timestamps in browser-local time
Spotted on the SFM webapp event modal — "Received by server at" was
showing the raw ISO string "2026-05-27T21:59:57.213043Z" because we
were assigning ev.timestamp / src.captured_at directly to the
textContent of the modal fields, bypassing the existing _fmtTs()
helper that wraps them in toLocaleString().
Net effect for operators: confusing "21:59 vs it's 6 PM" mismatch
when the displayed UTC timestamp didn't match wall-clock time. The
values were always correct; the display was just ambiguous.
After this fix:
- "Recorded at" (naive ISO from BW = unit local time) renders
cleanly as the unit wrote it: "5/27/2026, 6:00:13 AM"
- "Received by server at" (UTC with Z suffix) converts to browser
local: "5/27/2026, 5:59:57 PM"
- Timestamp column in the history table already used _fmtTs —
unchanged
- Same fix applied to the standalone /events page (sidebar event
list + meta header) via a new _fmtTsLocal helper
Note: did NOT add file-mtime-on-watcher-PC tracking as a separate
"Called in at" column — discussed and decided created_at is close
enough for schedule-compliance monitoring (worst case lag = watcher
poll interval ~60s, indistinguishable from BW write time at the
operationally-relevant resolution).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+12
-2
@@ -356,6 +356,16 @@ function _psiToDbl(psi) {
|
||||
return 20 * Math.log10(psi / DBL_REF);
|
||||
}
|
||||
|
||||
// Format an ISO timestamp in the browser's local timezone — UTC values
|
||||
// (with 'Z' suffix) convert; naive values are interpreted as local clock.
|
||||
// Returns '—' for null/empty/unparseable.
|
||||
function _fmtTsLocal(iso) {
|
||||
if (!iso) return '—';
|
||||
const d = new Date(iso);
|
||||
if (isNaN(d)) return iso;
|
||||
return d.toLocaleString();
|
||||
}
|
||||
|
||||
// Adaptive decimal formatter — scientific notation only for truly extreme
|
||||
// values. Normal-range peaks render as plain decimals with sensible
|
||||
// precision (was previously forcing toExponential(3) which produced ugly
|
||||
@@ -458,7 +468,7 @@ function renderEventList() {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'event-row' + (ev.false_trigger ? ' false_trigger' : '');
|
||||
if (ev.id === currentEventId) row.className += ' active';
|
||||
const ts = (ev.timestamp || '').replace('T', ' ').replace('Z', '');
|
||||
const ts = _fmtTsLocal(ev.timestamp);
|
||||
const pvs = ev.peak_vector_sum != null ? `${ev.peak_vector_sum.toFixed(3)} in/s` : '—';
|
||||
row.innerHTML = `
|
||||
<div class="er-top">
|
||||
@@ -510,7 +520,7 @@ function renderMeta(data, ev) {
|
||||
const metaDiv = document.getElementById('event-meta');
|
||||
const fields = [
|
||||
['Serial', data.serial || ev?.serial || '—'],
|
||||
['Timestamp', (data.timestamp || ev?.timestamp || '—').replace('T', ' ').replace('Z', '')],
|
||||
['Timestamp', _fmtTsLocal(data.timestamp || ev?.timestamp)],
|
||||
['Record', data.record_type || ev?.record_type || '—'],
|
||||
['Sample rate', data.sample_rate ? `${data.sample_rate} sps` : '—'],
|
||||
['Geo range', data.geo_range ? `${data.geo_range} (${data.geo_full_scale_ips} in/s FS)` : '—'],
|
||||
|
||||
Reference in New Issue
Block a user