雜記 › 技術筆記 › 相對時間的「一個月」寫死 30 天:時間戳頁六檔除數、13 位毫秒輸入與 UTC+8/UTC-5 兩檔截斷實測

相對時間的「一個月」寫死 30 天:時間戳頁六檔除數、13 位毫秒輸入與 UTC+8/UTC-5 兩檔截斷實測

時間戳頁的相對時間只用兩把寫死的除數:30 天算一個月、365 天算一年;六檔切換點、11 位與 12 位被當毫秒、slice(0,16) 兩檔截斷的逐字實測。

一個月在這頁只有 30 天那麼長

起因是 29 天前的記錄寫「29 天前」、30 天前的寫「1 個月前」,中間沒有「一個月零幾天」。為找切換點,我把這頁腳本單獨跑了一輪:模板 341 行 / 12362 位元組,腳本塊原樣執行,DOM 只給按 id 建的樁,時區在命令列釘成兩檔(getTimezoneOffset 實測 -480 分鐘與 +300 分鐘)。

被測的是時間戳轉換這一頁,相對時間掛在第一個 tab 的結果區裡。

六檔除數是寫死的常數

relativeTime(L285–L295)是一條 if 鏈,除數依次 1000 / 60000 / 3600000 / 86400000 / 2592000000 / 31536000000,沒有日曆參與:2592000000 除以 86400000 得 30,年檔同理得 365,「一個月」在這頁恆等於 30 天。交界讀數:

diff(毫秒)讀數
59999 / 6000059 秒前 / 1 分鐘前
86399999 / 8640000023 小時前 / 1 天前
2591999999 / 259200000029 天前 / 1 個月前
31535999999 / 3153600000012 個月前 / 1 年前

兩檔時區各跑一遍,表的差值與讀數逐字相同,只有起點 ts 差 46800000 毫秒。過去與未來只差後綴:數字出自 Math.abs,方向出自 sign 的 diff>=0,diff=0 落「0 秒前」。

跨月與跨閏年各讀到什麼

28 天(2419200000 毫秒)讀「28 天前」,29 天讀「29 天前」,30 天整才第一次見「1 個月前」,31 天(2678400000)仍只讀「1 個月前」;跨閏的 366 天與平年 365 天同讀「1 年前」。13 個 30 天(33696000000 毫秒)已落年檔,字面「13 個月」不可達。按真實日曆加月份是另一頁:時間加減計算。

日曆差值 → relativeTime 讀數 2592000000 / 30 天 1 個月前 2678400000 / 31 天 1 個月前 2419200000 / 28 天 28 天前 31536000000 / 365 天 1 年前 31622400000 / 366 天 1 年前 31449600000 / 364 天 12 個月前 月檔與年檔只按常數除數取整。

圖 1:跨月與跨閏的讀數階梯,30 天與 31 天落在同一格。

位數識別不是 10 與 13 兩檔

輸入側(L306–L309)先 parseInt(val,10),再判 String(ts).length<=10 才乘 1000:識別按位數走,卻不是那兩檔。13 位「1717200000123」不乘,iso 出「2024-06-01T00:00:00.123Z」;11 位與 12 位同樣被當毫秒,落 1970-07-18 與 1975-06-11。負數最彆扭:「-1717200000」的負號佔進 String 長度成 11,落 1969-12-12T03:00:00.000Z,9 位「-171720000」反而正確走秒檔——分界在位數,不在正負。

早退只有三條:空串藏掉結果區,NaN 彈 toast「請輸入有效的時間戳數字」,越界彈「無效的時間戳」,後兩條只彈提示、六格殘留上一輪讀數。其餘連提示都沒有:「20240531」當秒乘 1000 落 1970-08-23T06:22:11.000Z。

「填入現在」丟掉的正好是秒

fillNowDt(L323–L328)先按 getTimezoneOffset()*60000 平移再 slice(0,16)。取 FIXED_NOW=1717214437500:UTC+8 檔平移後是 24 字元「2024-06-01T12:00:37.500Z」,截成 16 字元、丟 8 字元,回跑得 1717214400,比真實秒 1717214437 低 37 秒;UTC-5 檔同值截出「2024-05-31T23:00」,回跑同為 1717214400。同一日期框串「2024-06-01T00:00」兩檔給 1717171200 與 1717218000,差 46800 秒即 13 小時,全頁沒有時區控件(grep select 命中 0 處)。

一次 slice(0,16) 的字元帳 UTC+8 檔 UTC-5 檔 24 字元 2024-06-01T12:00:37.500Z 24 字元 2024-05-31T23:00:37.500Z 截出 16 字元 2024-06-01T12:00 截出 16 字元 2024-05-31T23:00 回跑 1717214400,低 37 秒 回跑 1717214400

圖 2:截斷前後各剩幾字元,與兩檔偏移的差。

取整方向與往還原差多少

Math.floor 向負無窮取整:FIXED_NOW=-500 時秒格讀「-1」,-1500 讀「-2」,-1717200000500 讀「-1717200001」,比截絕對值少 1;這串回填後再餵回去,因長度 11 又被當毫秒,讀「54 年後」。同檔內往返乾淨:「1717200000」回填日期框再回跑差 0,「1717200000123」的毫秒只進 iso 不進本地框,回填後尾數歸零、差 123。

多次行為與沒測到的

計時器樁實測 setInterval 只註冊 1 條、間隔 1000 毫秒(L270);NOW 推進 86400000 後,同一個 ts=1717200000000 從「0 秒前」變「1 天前」,這格每次現讀、不緩存。真實跳字與渲染次數本輪純 Node 無渲染器,標未實測。

文案與代碼差在哪兩條

config/tools.php 的 timestamp 條目(L102–L115)逐條判,10 條裡 [一致] 6、[部分一致] 2、[衝突] 2。衝突兩條:description(L111)的「多時區選擇」——grep select 命中 0 處,日期框只是 type="datetime-local";intro(L112)連同模板 L51 的標籤「北京時間(Asia/Shanghai)」——那格走本機 getter,UTC-5 檔實測顯示「2024-05-31 19:00:00」。部分一致是 summary「支援時區」與佔位符位數口徑;本頁只讀本機 Date。日期按月份加減落到哪一天,站內舊文寫過一次,這裡不複述。

常見問題

Q:為什麼 31 天前的記錄只寫「1 個月前」?

月檔除數寫死為 2592000000 毫秒即 30 天,超出部分按常數取整。

Q:13 位毫秒要不要先自己換算?

不用換算,但它不只認 13 位:判據是不超過 10 位才乘 1000,11 位與 12 位一樣被當毫秒。

Q:結果區的數一定是這一輪的嗎?

不一定,非法輸入只彈提示,六格殘留上一輪讀數。

📎 相關文章

星號段長寫死:脫敏頁五套掩碼固定切出 4、3、11、12 枚星號,18 碼元串被手機檔搶走、空框回落 * 2026-11-13 鎖是單向的、百分比是兩把尺:pct 50 得 619×430,PNG 四檔 quality 全 6971 B 2026-11-13 兩個 tab 各算一遍同一個 C(n,r):n=20 寫滿 40 個因子、n=21 一格不剩,300 位大數與 r=0 的兩種讀法 2026-11-13 8 個元素算得出 40320 條卻只列 5000 行:排列計算器的兩道互不相干的上限、重複元素按 3! 計數與 n≤20 的過程斷點 2026-11-12 投放連結的裝配順序與大小寫摺疊:UTM 產生器六態標註、兩道硬上限與五欄各用一把尺實測 2026-11-12