Files

89 lines
5.1 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
// 共用時間與日誌工具:所有訊息輸出統一為 [yyyy/MM/dd HH:mm:ss][階段][等級]: 訊息(Asia/Taipei)。
/**
* 將指定時間轉為台北時區(Asia/Taipei)的顯示字串,格式固定為 yyyy/MM/dd HH:mm:ss24 小時制)。
* 利用 sv-SE 語系的 toLocaleString 產生 yyyy-MM-dd HH:mm:ss 後再把「-」換成「/」,
* 輸出不受執行環境(CI runner/主機)系統時區影響。
*
* @param {Date} [date=new Date()] 要格式化的時間;省略時使用現在時間。
* 須為有效的 Date 物件;傳入 Invalid Date 會得到 "Invalid Date" 字串(不丟例外),
* 傳入非 Date 型別屬誤用,可能丟出 TypeError。
* @returns {string} 台北時區的時間字串,格式 yyyy/MM/dd HH:mm:ss(例如 "2026/07/17 14:30:05")。
* @remarks
* 使用情境:log() 每次輸出日誌時呼叫本函式產生時間戳前綴;
* taipeiFromIso() 也在解析 ISO 字串成功後委派給本函式做最終格式化。
* 前置條件:無(純函式、無副作用);需要固定顯示格式的時間字串時皆可直接呼叫。
* 注意:格式與 JSC 規範「更新時間一律 Asia/Taipei、yyyy/MM/dd HH:mm:ss」一致,勿自行改動分隔符號。
*/
function taipeiNow(date = new Date()) {
return date
.toLocaleString('sv-SE', { timeZone: 'Asia/Taipei', hour12: false })
.replace(/-/g, '/');
}
/**
* 產生檔名用的台北時區時間戳,格式固定為 yyyy-MM-dd-HH:mm:ss24 小時制),
* 即把 sv-SE 格式(yyyy-MM-dd HH:mm:ss)中的空白換成「-」,避免檔名含空白。
* 主要供 AI review findings 輸出檔的檔名命名使用。
*
* @param {Date} [date=new Date()] 要格式化的時間;省略時使用現在時間。
* 須為有效的 Date 物件;傳入 Invalid Date 會得到 "Invalid-Date" 字串(不丟例外),
* 傳入非 Date 型別屬誤用,可能丟出 TypeError。
* @returns {string} 檔名用時間戳字串,格式 yyyy-MM-dd-HH:mm:ss(例如 "2026-07-17-14:30:05")。
* @remarks
* 使用情境:產生 findings 檔案(如 .gitea/ai-review 下的輸出檔)時呼叫,
* 讓檔名帶有可排序的建立時間。前置條件:無(純函式、無副作用)。
* 注意:輸出仍含「:」字元,在 Linux 檔名合法,但不可移植到 Windows 檔案系統;
* 若未來需跨平台檔名,需另行替換「:」。
*/
function taipeiFileStamp(date = new Date()) {
return date
.toLocaleString('sv-SE', { timeZone: 'Asia/Taipei', hour12: false })
.replace(' ', '-');
}
/**
* 將 ISO 8601 時間字串轉為台北時區(Asia/Taipei)的顯示字串(yyyy/MM/dd HH:mm:ss);
* 輸入為空或無法解析時回傳佔位符「—」,不丟例外,適合直接嵌入報表或留言等顯示用文字。
*
* @param {string | null | undefined} iso ISO 8601 時間字串(例如 "2026-07-17T06:30:05Z")。
* 可為 nullundefined/空字串,皆視為無資料而回傳「—」。
* 實作上接受任何 Date 建構子可解析的輸入,但非 ISO 格式的解析結果依 JS 引擎而異,建議一律傳 ISO 字串。
* @returns {string} 台北時區時間字串(yyyy/MM/dd HH:mm:ss),或無法解析時的佔位符 "—"。
* @remarks
* 使用情境:顯示外部系統(如 Gitea API、AI 服務回應)帶回的 UTC/ISO 時間欄位時呼叫,
* 統一轉成台北時區給人閱讀;來源欄位可能缺值,故以「—」佔位而非丟例外。
* 前置條件:無;結果僅供顯示,不應再拿去做時間運算(需運算請直接使用原始 ISO 值)。
*/
function taipeiFromIso(iso) {
if (!iso) return '—';
const date = new Date(iso);
return Number.isNaN(date.getTime()) ? '—' : taipeiNow(date);
}
/**
* 以專案統一格式輸出一行日誌到標準輸出:[yyyy/MM/dd HH:mm:ss][階段][等級]: 訊息,
* 時間戳固定為台北時區(Asia/Taipei)24 小時制;stage 為空時整個 [階段] 區塊省略。
*
* @param {string | null | undefined} stage 階段名稱(例如 "步驟1"、"收尾");
* 傳空字串/nullundefined 時省略 [階段] 區塊。
* @param {string} level 日誌等級,約定限 "INF"、"WRN"、"ERR"、"TRC"、"DBG" 五種;
* 程式碼未驗證,傳入其他字串會原樣輸出,遵守約定由呼叫端負責。
* @param {string} message 日誌訊息內容;非字串會被隱式轉字串(物件會變成 "[object Object]"),
* 請由呼叫端先自行序列化。
* @returns {void} 無回傳值;副作用為寫一行到 stdout。
* @remarks
* 使用情境:action 執行過程中的所有訊息輸出都應改呼叫本函式而非直接 console.log
* 讓 CIGitea Actions)log 具備一致的時間戳與等級標記、一行一則。
* 注意:所有等級(含 ERR)都輸出到 stdout 而非 stderr;此格式對應 JSC 的
* spec-time-log 輸出規範,勿自行變更括號與冒號排版。
*/
function log(stage, level, message) {
const stagePart = stage ? `[${stage}]` : '';
console.log(`[${taipeiNow()}]${stagePart}[${level}]: ${message}`);
}
module.exports = { taipeiNow, taipeiFileStamp, taipeiFromIso, log };