FR-121 子卡 .5,與 .4 同時派,都等 .1/.2/.3 驗收通過後。決策者 2026-09-27 裁:資料庫存 UTC、UI 依使用者所在地顯示,這版先寫死 Asia/Taipei、不做 profile;業界做法是前端依使用者時區轉換,本卡先把前端顯示點收成一支函式,之後 profile 接上只改那一處。
後端(.4)改完後 API 回的時間字串一律帶時區偏移(這版是台北 +08:00)。前端要做的是:所有顯示時間的地方都走同一支格式化函式,那支函式這版什麼都不轉、照字串顯示;前端任何自己解析時間、自己假設時區的寫法都要收掉。這樣現在 UI 一定是台北時間,之後做 profile 時只改那一支函式。
git grep -n "moment(\|new Date(\|toLocale\|formatDate\|dateFormat\|showTime\|created_at\|updated_at\|_time\b" -- src,把每個時間顯示點與輸入點列表(檔:行、欄位、目前寫法),分三類:① 純顯示 ② 日期輸入(Calendar)③ date-only 欄位(公告到期日這類)。列表進回寫。src/utils/dateTime.js 的一支 formatDateTime(isoString, pattern)(沒有就建,有就收斂)。這版實作:解析帶偏移的 ISO 字串後以字串自帶的偏移格式化(moment.parseZone 或 dayjs utcOffset),不呼叫 local()/不用瀏覽器時區。裡面留一個 getDisplayTimeZone() 先固定回 'Asia/Taipei' 並註解「profile 設定接這裡」。YYYY-MM-DDTHH:mm:ss+08:00)或明確送 date-only(YYYY-MM-DD)並與後端 schema 對齊,不再送 naive 字串。YYYY-MM-DD(memory feedback_date_only_utc_midnight_display_bug 的坑)。new Date(str) 後再 toLocaleString/getHours 的二次轉換;任何硬拆字串取年月日的寫法。vite build 通過;盤點表每一列標「已收斂到 formatDateTime/date-only 不轉/輸入帶偏移」。+08:00 改成 +00:00(或 mock),畫面顯示要跟著變 8 小時——證明前端沒有自己假設時區、是照字串顯示。再把瀏覽器時區改成 America/New_York 重整,畫面不變——證明沒吃瀏覽器時區。