← back to Trending Dw
trending: pin staleness to Steve's TZ (America/Los_Angeles) so prod (UTC) and Mac2 agree
c9753c24134a4e17d956bff9105e6e3d4c5dac16 · 2026-07-06 21:40:47 -0700 · Steve Abrams
Kamatera runs UTC, so the prior local-time staleDays showed 'as of Jul 6 · 1 day ago'
on prod while Mac2 showed 'today'. spottedAt is a bare date with no tz — pin the
reference to PT + pure Date.UTC arithmetic on both endpoints. Verified staleDays=0
under PT, UTC, and Tokyo. Fixed in server.js loadMeta + tick.sh.
Files touched
M scripts/tick.shM server.js
Diff
commit c9753c24134a4e17d956bff9105e6e3d4c5dac16
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon Jul 6 21:40:47 2026 -0700
trending: pin staleness to Steve's TZ (America/Los_Angeles) so prod (UTC) and Mac2 agree
Kamatera runs UTC, so the prior local-time staleDays showed 'as of Jul 6 · 1 day ago'
on prod while Mac2 showed 'today'. spottedAt is a bare date with no tz — pin the
reference to PT + pure Date.UTC arithmetic on both endpoints. Verified staleDays=0
under PT, UTC, and Tokyo. Fixed in server.js loadMeta + tick.sh.
---
scripts/tick.sh | 6 ++++--
server.js | 14 ++++++++------
2 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/scripts/tick.sh b/scripts/tick.sh
index 7598eab..afa848e 100755
--- a/scripts/tick.sh
+++ b/scripts/tick.sh
@@ -33,9 +33,11 @@ read AS_OF STALE_DAYS IMG_GAP COUNT <<<"$("$NODE" -e '
try{ const d=JSON.parse(require("fs").readFileSync("data/bestsellers.json","utf8"));
const its=d.items||[]; const sp=its.map(i=>i.spottedAt).filter(Boolean).sort();
const asOf=sp.length?sp[sp.length-1]:(d.generatedAt||"");
- // local whole-day diff (Date.parse of YYYY-MM-DD is UTC midnight → off-by-one after 00:00 UTC)
+ // staleness pinned to Steve TZ (America/Los_Angeles) + pure date arithmetic, so it matches
+ // the server (Kamatera/UTC) and Mac2 identically. spottedAt is a bare date with no tz.
let days=-1; { const p=String(asOf).split("-").map(Number);
- if(p[0]&&p[1]&&p[2]){ const t=new Date(p[0],p[1]-1,p[2]),n=new Date(),td=new Date(n.getFullYear(),n.getMonth(),n.getDate()); days=Math.round((td-t)/86400000); } }
+ if(p[0]&&p[1]&&p[2]){ const ts=new Date().toLocaleDateString("en-CA",{timeZone:"America/Los_Angeles"}).split("-").map(Number);
+ days=Math.round((Date.UTC(ts[0],ts[1]-1,ts[2])-Date.UTC(p[0],p[1]-1,p[2]))/86400000); } }
const gap=its.filter(i=>!i.image).length;
process.stdout.write(asOf+" "+days+" "+gap+" "+its.length);
}catch(e){ process.stdout.write("- -1 -1 0"); }' 2>/dev/null)"
diff --git a/server.js b/server.js
index 9d56c4f..def3d49 100644
--- a/server.js
+++ b/server.js
@@ -42,12 +42,14 @@ function loadMeta(){
// most recent spottedAt across items = the real "as of" date
const spotted = items.map(i=>i.spottedAt).filter(Boolean).sort();
const asOf = spotted.length ? spotted[spotted.length-1] : generatedAt;
- // whole-day diff in LOCAL time — parsing 'YYYY-MM-DD' via Date.parse() treats it as UTC
- // midnight, so a today-local date read after 00:00 UTC would wrongly show "1 day ago".
- const staleDays = asOf ? (()=>{ const [y,m,d]=String(asOf).split('-').map(Number);
- if(!y||!m||!d) return null;
- const then=new Date(y,m-1,d), now=new Date(), today=new Date(now.getFullYear(),now.getMonth(),now.getDate());
- return Math.round((today-then)/86400000); })() : null;
+ // staleness must be identical whether this runs on Mac2 (Pacific) or Kamatera (UTC), so
+ // pin the reference to Steve's timezone and do pure date arithmetic (Date.UTC on both
+ // endpoints — no tz drift). spottedAt is a bare 'YYYY-MM-DD' with no tz of its own.
+ const staleDays = asOf ? (()=>{ const p=String(asOf).split('-').map(Number);
+ if(!p[0]||!p[1]||!p[2]) return null;
+ const todayStr = new Date().toLocaleDateString('en-CA', { timeZone: 'America/Los_Angeles' }); // YYYY-MM-DD in PT
+ const t = todayStr.split('-').map(Number);
+ return Math.round((Date.UTC(t[0],t[1]-1,t[2]) - Date.UTC(p[0],p[1]-1,p[2])) / 86400000); })() : null;
const withImage = items.filter(i=>i.image).length;
return { generatedAt, asOf, staleDays, count: items.length, imagesAttachedAt: d.imagesAttachedAt || null,
withImage, imageGap: items.length - withImage, source: d.source || null };
← 74b3db6 trending: LIVE refresh — 49 current 2026 bestsellers via Exa
·
back to Trending Dw
·
trending: self-host all card images locally (scripts/localiz 00c19c0 →