fix(badges): display print timestamps in local browser time instead of UTC

The backend returns naive UTC datetime strings (no Z suffix). dayjs was treating
them as local time, so staff in ET saw UTC times. Added treat_as_utc param to
iso_datetime_formatter (default false, zero breakage to existing call sites) that
appends Z before parsing so dayjs converts to browser-local time on display.

Applied to all print timestamp call sites in badge list (first/last print visible
row + debug row) and to created_on/updated_on in the debug row.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-06-09 07:56:46 -04:00
parent b8ceed69d0
commit 6e04145514
2 changed files with 29 additions and 11 deletions

View File

@@ -27,7 +27,11 @@ export const iso_datetime_formatter = function iso_datetime_formatter(
named_format: string = 'datetime_iso_no_seconds', // date_iso, datetime_iso_no_seconds named_format: string = 'datetime_iso_no_seconds', // date_iso, datetime_iso_no_seconds
// Pass true/false to resolve to the correct 12h or 24h variant automatically. // Pass true/false to resolve to the correct 12h or 24h variant automatically.
// null (default) leaves named_format unchanged — all existing call sites unaffected. // null (default) leaves named_format unchanged — all existing call sites unaffected.
use_12h: boolean | null = null use_12h: boolean | null = null,
// When true, treats a naive datetime string (no Z / offset) as UTC so dayjs
// converts it to local browser time on display. Use for timestamps stored as
// UTC in the DB but returned without a timezone indicator.
treat_as_utc: boolean = false
) { ) {
// console.log('*** iso_datetime_formatter() ***'); // console.log('*** iso_datetime_formatter() ***');
@@ -74,6 +78,12 @@ export const iso_datetime_formatter = function iso_datetime_formatter(
raw_datetime = new Date(); // Get the current datetime if one was not passed. raw_datetime = new Date(); // Get the current datetime if one was not passed.
} }
// Append 'Z' to naive UTC strings so dayjs converts to local browser time.
// Guards against double-appending if the backend ever adds timezone info.
if (treat_as_utc && typeof raw_datetime === 'string' && !raw_datetime.match(/Z$|[+-]\d{2}:?\d{2}$/)) {
raw_datetime = raw_datetime + 'Z';
}
if (use_12h !== null) { if (use_12h !== null) {
named_format = use_12h named_format = use_12h
? (TO_12H[named_format] ?? named_format) ? (TO_12H[named_format] ?? named_format)

View File

@@ -275,13 +275,13 @@ let visible_badge_obj_li = $derived(
Checked in &middot; {print_count}&times; Checked in &middot; {print_count}&times;
{#if event_badge_obj.print_first_datetime} {#if event_badge_obj.print_first_datetime}
&middot; &middot;
{ae_util.iso_datetime_formatter(event_badge_obj.print_first_datetime, 'date_full_no_year')} {ae_util.iso_datetime_formatter(event_badge_obj.print_first_datetime, 'date_full_no_year', null, true)}
{ae_util.iso_datetime_formatter(event_badge_obj.print_first_datetime, 'time_12_long')} {ae_util.iso_datetime_formatter(event_badge_obj.print_first_datetime, 'time_12_long', null, true)}
{/if} {/if}
{#if event_badge_obj.print_last_datetime && event_badge_obj.print_last_datetime !== event_badge_obj.print_first_datetime} {#if event_badge_obj.print_last_datetime && event_badge_obj.print_last_datetime !== event_badge_obj.print_first_datetime}
&middot; last print: &middot; last print:
{ae_util.iso_datetime_formatter(event_badge_obj.print_last_datetime, 'date_full_no_year')} {ae_util.iso_datetime_formatter(event_badge_obj.print_last_datetime, 'date_full_no_year', null, true)}
{ae_util.iso_datetime_formatter(event_badge_obj.print_last_datetime, 'time_12_long')} {ae_util.iso_datetime_formatter(event_badge_obj.print_last_datetime, 'time_12_long', null, true)}
{/if} {/if}
</span> </span>
</div> </div>
@@ -451,11 +451,11 @@ let visible_badge_obj_li = $derived(
<span>Printed {print_count}&times;</span> <span>Printed {print_count}&times;</span>
{#if event_badge_obj.print_first_datetime} {#if event_badge_obj.print_first_datetime}
<span class="opacity-50">&middot;</span> <span class="opacity-50">&middot;</span>
<span>{ae_util.iso_datetime_formatter(event_badge_obj.print_first_datetime, 'date_full_no_year')} {ae_util.iso_datetime_formatter(event_badge_obj.print_first_datetime, 'time_12_long')}</span> <span>{ae_util.iso_datetime_formatter(event_badge_obj.print_first_datetime, 'date_full_no_year', null, true)} {ae_util.iso_datetime_formatter(event_badge_obj.print_first_datetime, 'time_12_long', null, true)}</span>
{/if} {/if}
{#if event_badge_obj.print_last_datetime && event_badge_obj.print_last_datetime !== event_badge_obj.print_first_datetime} {#if event_badge_obj.print_last_datetime && event_badge_obj.print_last_datetime !== event_badge_obj.print_first_datetime}
<span class="opacity-50">&middot;</span> <span class="opacity-50">&middot;</span>
<span>last print: {ae_util.iso_datetime_formatter(event_badge_obj.print_last_datetime, 'date_full_no_year')} {ae_util.iso_datetime_formatter(event_badge_obj.print_last_datetime, 'time_12_long')}</span> <span>last print: {ae_util.iso_datetime_formatter(event_badge_obj.print_last_datetime, 'date_full_no_year', null, true)} {ae_util.iso_datetime_formatter(event_badge_obj.print_last_datetime, 'time_12_long', null, true)}</span>
{/if} {/if}
</div> </div>
{/if} {/if}
@@ -472,14 +472,18 @@ let visible_badge_obj_li = $derived(
<span class="font-bold opacity-50">CR:</span> <span class="font-bold opacity-50">CR:</span>
{ae_util.iso_datetime_formatter( {ae_util.iso_datetime_formatter(
event_badge_obj.created_on, event_badge_obj.created_on,
'datetime_iso_12_no_seconds' 'datetime_iso_12_no_seconds',
null,
true
)} )}
</span> </span>
<span class="flex items-center gap-1"> <span class="flex items-center gap-1">
<span class="font-bold opacity-50">UP:</span> <span class="font-bold opacity-50">UP:</span>
{ae_util.iso_datetime_formatter( {ae_util.iso_datetime_formatter(
event_badge_obj.updated_on, event_badge_obj.updated_on,
'datetime_iso_12_no_seconds' 'datetime_iso_12_no_seconds',
null,
true
)} )}
</span> </span>
<span class="flex items-center gap-1"> <span class="flex items-center gap-1">
@@ -491,7 +495,9 @@ let visible_badge_obj_li = $derived(
{event_badge_obj.print_first_datetime {event_badge_obj.print_first_datetime
? ae_util.iso_datetime_formatter( ? ae_util.iso_datetime_formatter(
event_badge_obj.print_first_datetime, event_badge_obj.print_first_datetime,
'datetime_iso_12_no_seconds' 'datetime_iso_12_no_seconds',
null,
true
) )
: '—'} : '—'}
</span> </span>
@@ -500,7 +506,9 @@ let visible_badge_obj_li = $derived(
{event_badge_obj.print_last_datetime {event_badge_obj.print_last_datetime
? ae_util.iso_datetime_formatter( ? ae_util.iso_datetime_formatter(
event_badge_obj.print_last_datetime, event_badge_obj.print_last_datetime,
'datetime_iso_12_no_seconds' 'datetime_iso_12_no_seconds',
null,
true
) )
: '—'} : '—'}
</span> </span>