5690d61438
稽核輪(fix/console-truth-audit,總管派工+leo 三裁決): B1 時區:console 全站日期/時間顯示統一 Asia/Taipei(新 lib/taipei-time.ts, server 判定與頁面 client JS 同一套、等價性單測互鎖)。原本用瀏覽器本地 getter——換裝置就換日期;憑證頁改顯「日期+時分(台北)」:raw 1783349834 =台北 2026-07-06 22:57,7/6 顯示是真的,缺的是時分與固定時區。 B2 總庫數字(leo 裁:14-E 遺產 deprecated 不再顯示):總庫搜尋/設定頁頭部 統計改讀新 GET /console/kb-scale-data(精耕層 wiki 卡/三元組/已嵌入+最近 寫入,全 live、limit=1/stats 聚合不掃庫);搜尋仍搜全庫、結果 wiki_card 加「精耕」綠標;dashboard-data 的 entries 計數補 owner_id(原混租戶: 459,137 vs leo 458,732)。 B3 設定頁:vectorize 文案改具體事實(config kbdb_embed+acr update,#32 教訓)+live 狀態;新增 MCP token TTL 誠實佔位(GET /console/settings-data, 可調功能=Arcrun#19,不假裝能遠端改)。 C 今日完成/今日路線接 sprint 任務板(leo 拍板):比照 #36 等leo清單模式, 同一輪 Gitea fetch(90s CF cache 共用)解析「## 任務板」checkbox;「今日 完成」只認「完成(今天台北日)」標記,[x] 無日期不冒領;快取存原始 completed_days,跨台北午夜不誤算;板檔今天沒 commit → 頁面誠實標「今日 任務板未更新」;dash_task 降 fallback、燈號判定不變。parser 以 live 07a/07b 檔實測(標題抽取棄「首個粗體」——實讀發現會抽到完成註記)。 驗證:tsc exit 0;vitest 82/83(1 失敗=pre-existing executor「不存在的 零件」,stash 於 main 複驗同樣紅);新增 taipei-time 10 案例(含跨日/跨年/ client-server 等價/leo 實測 epoch)+任務板 parser 7 案例;兩頁 inline JS 渲染後抽 <script> node --check 全過(mistakes #24 手法)。
59 lines
3.2 KiB
TypeScript
59 lines
3.2 KiB
TypeScript
/**
|
||
* Asia/Taipei(UTC+8,無日光節約)時間顯示 helper(fix/console-truth-audit,2026-07-07)
|
||
*
|
||
* 為什麼存在:console 各頁原本用瀏覽器本地時區(`Date#getFullYear/getMonth/...`)渲染
|
||
* created_at / last_used_at / 刷新時間戳——顯示結果隨看的裝置漂移(換個時區的裝置就換一個
|
||
* 日期),且與駕駛艙 #36 的「台北日曆日」判定(`taipeiDayKey`)不是同一套。本檔把
|
||
* 「epoch ms → 台北牆鐘」收斂成唯一實作:
|
||
* - server 側(TS):dashboard-data 的台北日判定 import 這裡。
|
||
* - client 側(頁面 inline JS):console.ts / console-dashboard.ts 的 HTML 內嵌
|
||
* `TAIPEI_CLIENT_JS`(同邏輯的 ES5 版)。等價性由 tests/taipei-time.test.ts 用
|
||
* `new Function` 求值 client 版與 server 版逐案對照,防兩份漂移。
|
||
*
|
||
* 台北無 DST、固定 +8——用「UTC getter +固定位移」即可,不需要 Intl 時區資料庫。
|
||
*/
|
||
|
||
export const TAIPEI_OFFSET_MS = 8 * 3600 * 1000;
|
||
|
||
function pad2(n: number): string {
|
||
return String(n).padStart(2, '0');
|
||
}
|
||
|
||
/** 台北日曆日 key('YYYY-MM-DD')。#36 駕駛艙「這筆是不是今天寫的」判定用。 */
|
||
export function taipeiDayKey(ms: number): string {
|
||
return new Date(ms + TAIPEI_OFFSET_MS).toISOString().slice(0, 10);
|
||
}
|
||
|
||
/** 台北日期字串 'YYYY-MM-DD'(顯示用,同 taipeiDayKey,語意別名)。 */
|
||
export function taipeiDateStr(ms: number): string {
|
||
return taipeiDayKey(ms);
|
||
}
|
||
|
||
/** 台北日期+時分 'YYYY-MM-DD HH:MM'。 */
|
||
export function taipeiDateTimeStr(ms: number): string {
|
||
const d = new Date(ms + TAIPEI_OFFSET_MS);
|
||
return `${taipeiDayKey(ms)} ${pad2(d.getUTCHours())}:${pad2(d.getUTCMinutes())}`;
|
||
}
|
||
|
||
/** 台北時分秒 'HH:MM:SS'(駕駛艙「上次刷新」戳記用)。 */
|
||
export function taipeiTimeStr(ms: number): string {
|
||
const d = new Date(ms + TAIPEI_OFFSET_MS);
|
||
return `${pad2(d.getUTCHours())}:${pad2(d.getUTCMinutes())}:${pad2(d.getUTCSeconds())}`;
|
||
}
|
||
|
||
/**
|
||
* client 版(ES5,內嵌進 console 頁 inline <script>)。與上面 server 版同邏輯——
|
||
* 測試以 `new Function(TAIPEI_CLIENT_JS + 'return {...}')()` 求值後逐案對照 server 版,
|
||
* 任一份改了另一份沒跟上會直接紅。頁面內以 `${TAIPEI_CLIENT_JS}` 插入,故這裡不用
|
||
* template literal 語法(頁面本身是 template string,反引號會提前終結)。
|
||
*/
|
||
export const TAIPEI_CLIENT_JS = [
|
||
"var TAIPEI_OFFSET_MS = 28800000; // UTC+8,台北無 DST",
|
||
"function tpePad(n) { n = String(n); return n.length < 2 ? '0' + n : n; }",
|
||
"function taipeiDayKey(ms) { return new Date(ms + TAIPEI_OFFSET_MS).toISOString().slice(0, 10); }",
|
||
"function taipeiDateStr(ms) { return taipeiDayKey(ms); }",
|
||
"function taipeiDateTimeStr(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return taipeiDayKey(ms) + ' ' + tpePad(d.getUTCHours()) + ':' + tpePad(d.getUTCMinutes()); }",
|
||
"function taipeiTimeStr(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return tpePad(d.getUTCHours()) + ':' + tpePad(d.getUTCMinutes()) + ':' + tpePad(d.getUTCSeconds()); }",
|
||
"function taipeiMonthDay(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return { month: d.getUTCMonth() + 1, day: d.getUTCDate() }; }",
|
||
].join('\n');
|