From a72359432b8c85f4e95ce8b30adc2a83427fbf60 Mon Sep 17 00:00:00 2001 From: user Date: Sat, 28 Feb 2026 16:14:56 -0800 Subject: [PATCH] fix: include timezone offset in all displayed timestamps All isoDate() functions now output proper ISO 8601 format with timezone offset (e.g. 2026-02-28T15:30:00-08:00) instead of bare datetime strings. Also uses 'T' separator per ISO 8601. closes #116 --- src/popup/index.html | 6 ++++++ src/popup/views/addressDetail.js | 25 +++++++++++++++++++++++-- src/popup/views/addressToken.js | 25 +++++++++++++++++++++++-- src/popup/views/helpers.js | 25 +++++++++++++++++++++++-- src/popup/views/settings.js | 6 ++++++ src/shared/state.js | 4 ++++ 6 files changed, 85 insertions(+), 6 deletions(-) diff --git a/src/popup/index.html b/src/popup/index.html index 2c62210..2a46bef 100644 --- a/src/popup/index.html +++ b/src/popup/index.html @@ -913,6 +913,12 @@ /> gwei +
diff --git a/src/popup/views/addressDetail.js b/src/popup/views/addressDetail.js index 0e222cb..3ef4951 100644 --- a/src/popup/views/addressDetail.js +++ b/src/popup/views/addressDetail.js @@ -95,18 +95,39 @@ function show() { function isoDate(timestamp) { const d = new Date(timestamp * 1000); const pad = (n) => String(n).padStart(2, "0"); + if (state.utcTimestamps) { + return ( + d.getUTCFullYear() + + "-" + + pad(d.getUTCMonth() + 1) + + "-" + + pad(d.getUTCDate()) + + "T" + + pad(d.getUTCHours()) + + ":" + + pad(d.getUTCMinutes()) + + ":" + + pad(d.getUTCSeconds()) + + "Z" + ); + } + const offsetMin = -d.getTimezoneOffset(); + const sign = offsetMin >= 0 ? "+" : "-"; + const absOff = Math.abs(offsetMin); + const tzStr = sign + pad(Math.floor(absOff / 60)) + ":" + pad(absOff % 60); return ( d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate()) + - " " + + "T" + pad(d.getHours()) + ":" + pad(d.getMinutes()) + ":" + - pad(d.getSeconds()) + pad(d.getSeconds()) + + tzStr ); } diff --git a/src/popup/views/addressToken.js b/src/popup/views/addressToken.js index b0e926b..78b03c9 100644 --- a/src/popup/views/addressToken.js +++ b/src/popup/views/addressToken.js @@ -48,18 +48,39 @@ function etherscanAddressLink(address) { function isoDate(timestamp) { const d = new Date(timestamp * 1000); const pad = (n) => String(n).padStart(2, "0"); + if (state.utcTimestamps) { + return ( + d.getUTCFullYear() + + "-" + + pad(d.getUTCMonth() + 1) + + "-" + + pad(d.getUTCDate()) + + "T" + + pad(d.getUTCHours()) + + ":" + + pad(d.getUTCMinutes()) + + ":" + + pad(d.getUTCSeconds()) + + "Z" + ); + } + const offsetMin = -d.getTimezoneOffset(); + const sign = offsetMin >= 0 ? "+" : "-"; + const absOff = Math.abs(offsetMin); + const tzStr = sign + pad(Math.floor(absOff / 60)) + ":" + pad(absOff % 60); return ( d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate()) + - " " + + "T" + pad(d.getHours()) + ":" + pad(d.getMinutes()) + ":" + - pad(d.getSeconds()) + pad(d.getSeconds()) + + tzStr ); } diff --git a/src/popup/views/helpers.js b/src/popup/views/helpers.js index 1ad8ae0..450c98e 100644 --- a/src/popup/views/helpers.js +++ b/src/popup/views/helpers.js @@ -228,18 +228,39 @@ function formatAddressHtml(address, ensName, maxLen, title) { function isoDate(timestamp) { const d = new Date(timestamp * 1000); const pad = (n) => String(n).padStart(2, "0"); + if (state.utcTimestamps) { + return ( + d.getUTCFullYear() + + "-" + + pad(d.getUTCMonth() + 1) + + "-" + + pad(d.getUTCDate()) + + "T" + + pad(d.getUTCHours()) + + ":" + + pad(d.getUTCMinutes()) + + ":" + + pad(d.getUTCSeconds()) + + "Z" + ); + } + const offsetMin = -d.getTimezoneOffset(); + const sign = offsetMin >= 0 ? "+" : "-"; + const absOff = Math.abs(offsetMin); + const tzStr = sign + pad(Math.floor(absOff / 60)) + ":" + pad(absOff % 60); return ( d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate()) + - " " + + "T" + pad(d.getHours()) + ":" + pad(d.getMinutes()) + ":" + - pad(d.getSeconds()) + pad(d.getSeconds()) + + tzStr ); } diff --git a/src/popup/views/settings.js b/src/popup/views/settings.js index ea67337..525aec2 100644 --- a/src/popup/views/settings.js +++ b/src/popup/views/settings.js @@ -241,6 +241,12 @@ function init(ctx) { } }); + $("settings-utc-timestamps").checked = state.utcTimestamps; + $("settings-utc-timestamps").addEventListener("change", async () => { + state.utcTimestamps = $("settings-utc-timestamps").checked; + await saveState(); + }); + $("btn-main-add-wallet").addEventListener("click", ctx.showAddWalletView); $("btn-settings-add-token").addEventListener( diff --git a/src/shared/state.js b/src/shared/state.js index a98d84c..17cc4c8 100644 --- a/src/shared/state.js +++ b/src/shared/state.js @@ -23,6 +23,7 @@ const DEFAULT_STATE = { hideFraudContracts: true, hideDustTransactions: true, dustThresholdGwei: 100000, + utcTimestamps: false, fraudContracts: [], tokenHolderCache: {}, }; @@ -53,6 +54,7 @@ async function saveState() { hideFraudContracts: state.hideFraudContracts, hideDustTransactions: state.hideDustTransactions, dustThresholdGwei: state.dustThresholdGwei, + utcTimestamps: state.utcTimestamps, fraudContracts: state.fraudContracts, tokenHolderCache: state.tokenHolderCache, currentView: state.currentView, @@ -108,6 +110,8 @@ async function loadState() { saved.dustThresholdGwei !== undefined ? saved.dustThresholdGwei : 100000; + state.utcTimestamps = + saved.utcTimestamps !== undefined ? saved.utcTimestamps : false; state.fraudContracts = saved.fraudContracts || []; state.tokenHolderCache = saved.tokenHolderCache || {}; state.currentView = saved.currentView || null;