[object Object]

← back to build-pages

api/fleet/today: default to America/Los_Angeles (Steve's tz), ?tz= override — UTC at PT-evening was misleading

1db956df827ba401b0382364cc980d7487c8cfb5 · 2026-05-13 20:07:53 -0700 · SteveStudio2

Files touched

Diff

commit 1db956df827ba401b0382364cc980d7487c8cfb5
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Wed May 13 20:07:53 2026 -0700

    api/fleet/today: default to America/Los_Angeles (Steve's tz), ?tz= override — UTC at PT-evening was misleading
---
 server.js | 31 ++++++++++++++++++++++++-------
 1 file changed, 24 insertions(+), 7 deletions(-)

diff --git a/server.js b/server.js
index 1da5964..51fd076 100644
--- a/server.js
+++ b/server.js
@@ -736,14 +736,31 @@ app.get('/p/:slug/c/:hash', async (req, res, next) => {
 });
 
 // /api/fleet/today — every commit landed on the calendar date passed via
-// ?date=YYYY-MM-DD, or today (server-local) if omitted. Different from
-// /api/recent?since=24h because it's a calendar boundary, not rolling.
+// ?date=YYYY-MM-DD, or today (Pacific Time, Steve's zone) if omitted.
+// Different from /api/recent?since=24h because it's a calendar boundary,
+// not rolling. ?tz= can override (e.g. ?tz=UTC).
 app.get('/api/fleet/today', async (req, res, next) => {
+  const tz = typeof req.query.tz === 'string' ? req.query.tz : 'America/Los_Angeles';
+  // sv-SE locale gives YYYY-MM-DD HH:MM:SS — easy slice + reliable boundary.
+  const todayLocal = new Date().toLocaleString('sv-SE', { timeZone: tz }).slice(0, 10);
   const dateStr = (typeof req.query.date === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(req.query.date))
     ? req.query.date
-    : new Date().toISOString().slice(0, 10);
-  const start = new Date(dateStr + 'T00:00:00');
-  const end   = new Date(start.getTime() + 86400000);
+    : todayLocal;
+  // Build the start-of-day in the target timezone, in ms. The trick is to
+  // ask the same Date what midnight in that tz translates to in epoch ms —
+  // we form the local-midnight string and pass it through Date.parse with the
+  // tz offset implicit. Simpler approach: parse 00:00 in tz via formatToParts.
+  const startMs = (() => {
+    // YYYY-MM-DDT00:00:00 plus the tz offset on that date, computed by
+    // formatting the candidate midnight in target tz and seeing how far it
+    // drifts from UTC. Off-by-DST safe within ±1h, fine for our use.
+    const probe = new Date(`${dateStr}T00:00:00Z`);
+    const fmt = probe.toLocaleString('sv-SE', { timeZone: tz, hour12: false }).slice(0, 19).replace(' ', 'T');
+    const fromTz = new Date(fmt).getTime();
+    const offset = probe.getTime() - fromTz;
+    return new Date(`${dateStr}T00:00:00Z`).getTime() + offset;
+  })();
+  const endMs = startMs + 86400000;
   try {
     const all = [];
     for (const [slug, p] of Object.entries(PROJECTS)) {
@@ -751,14 +768,14 @@ app.get('/api/fleet/today', async (req, res, next) => {
       if (!bundle) continue;
       for (const c of bundle.commits) {
         const t = new Date(c.dateISO).getTime();
-        if (t >= start.getTime() && t < end.getTime()) {
+        if (t >= startMs && t < endMs) {
           all.push({ slug, project: p.name, hash: c.shortHash, date: c.dateISO, author: canonAuthor(c.author), subject: c.subject });
         }
       }
     }
     all.sort((a, b) => (b.date < a.date ? -1 : b.date > a.date ? 1 : 0));
     res.setHeader('Cache-Control', 'public, max-age=30, stale-while-revalidate=60');
-    res.json({ date: dateStr, total: all.length, results: all });
+    res.json({ date: dateStr, tz, total: all.length, results: all });
   } catch (e) { next(e); }
 });
 

← e0c1cd7 api: /api/fleet/today — calendar-day commit slice (different  ·  back to build-pages  ·  home: today-PT counter (Steve's calendar day, not rolling-24 b709184 →