/** * CS Reporting — shared datetime display for tables and audit/history UI. * * API/SQL timestamps from SYSUTCDATETIME (and similar) are UTC. Naive ISO strings * without Z/offset must be treated as UTC, then shown in the browser's local timezone. * * Today (browser local calendar day): time only (HH:mm:ss). * Other days: YYYY-MM-DD HH:mm:ss (no ISO "T" separator). */ (function (global) { 'use strict'; function pad2(n) { return n < 10 ? '0' + n : String(n); } /** * Parse API/SQL datetime as an absolute instant. * Naive values (no Z / offset) are assumed UTC — matching SQL SYSUTCDATETIME storage. */ function parseDateTime(value) { if (value === null || value === undefined) { return null; } let text = String(value).trim(); if (!text) { return null; } // Date-only: treat as UTC midnight so local display is consistent. if (/^\d{4}-\d{2}-\d{2}$/.test(text)) { const dOnly = new Date(text + 'T00:00:00Z'); return Number.isNaN(dOnly.getTime()) ? null : dOnly; } const hasZone = /[zZ]$/.test(text) || /[+-]\d{2}:?\d{2}$/.test(text); if (text.indexOf(' ') >= 0 && text.indexOf('T') < 0) { text = text.replace(' ', 'T'); } // Strip trailing whitespace-only; keep fractional seconds; append Z when naive. if (!hasZone && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(text)) { text = text + 'Z'; } const d = new Date(text); if (Number.isNaN(d.getTime())) { return null; } return d; } function isSameLocalDay(a, b) { return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate(); } /** * @param {string|null|undefined} value ISO or SQL datetime from API (UTC when naive) * @param {string} [emptyFallback='—'] shown when value is empty * @returns {string} Browser-local display string */ function formatDisplayDateTime(value, emptyFallback) { const fallback = emptyFallback === undefined ? '—' : emptyFallback; const d = parseDateTime(value); if (!d) { if (value === null || value === undefined || String(value).trim() === '') { return fallback; } return String(value); } // getHours/getDate use the viewer's local timezone (e.g. GMT/BST in the UK). const hh = pad2(d.getHours()); const mm = pad2(d.getMinutes()); const ss = pad2(d.getSeconds()); const now = new Date(); if (isSameLocalDay(d, now)) { return hh + ':' + mm + ':' + ss; } const yyyy = d.getFullYear(); const mo = pad2(d.getMonth() + 1); const dd = pad2(d.getDate()); return yyyy + '-' + mo + '-' + dd + ' ' + hh + ':' + mm + ':' + ss; } global.CSReporting = global.CSReporting || {}; global.CSReporting.formatDisplayDateTime = formatDisplayDateTime; global.CSReporting.parseApiDateTime = parseDateTime; }(typeof window !== 'undefined' ? window : this));