同一張訂單,成立時間、應付金額與配送備註,看起來只是幾個欄位。時間換個時區、金額換個取位數方式,或把沒提供的備註當成要求清空,後續處理就可能得到不同結果。
昨天談快取時,問題是讀到的資料可能已經過時。今天換個角度:即使資料是新的,程式保存、計算與顯示的方式,還能不能表達它原本的意思?
日期:同一天,不一定是同一個時間點
例如台灣時間10月1日凌晨零點半成立的訂單,換成UTC,也就是協調世界時,仍是9月30日。營運人員查詢「10月1日的訂單」時,如果程式直接用Server的日期篩選,這筆訂單可能就不見了;結果取決於這份查詢採用哪個時區。
訂單成立時間代表實際發生的時間點,可以用UTC保存,再依需求轉換顯示。至於同一份營運報表要依使用者所在地,還是固定依台灣時間分日,答案來自報表的用途,不會由儲存格式自動決定。
生日則不同。我們在意的是哪年哪月哪日,而不是當天幾點;保存年月日就足夠了。若替它補上00:00,再當成時間點跨時區轉換,畫面上的生日反而可能變成前一天或是後一天。
金額:算得出來,還要確認怎麼算、怎麼顯示
金額經過前端、API與資料庫,每一站使用的數值型別與轉換方式,都可能影響結果。同一種語言選不同型別,也可能算出不同數字。二進位浮點數無法精確表示某些十進位小數,因此金額常以十進位數值型別計算,或依約定單位保存整數。
型別選對,計算順序仍會帶來差異。假設兩筆明細各為10.05元、都打九折,且規則採一般四捨五入至小數第二位:逐筆計算會各得到9.05元,合計18.10元;先加總再打折,則得到18.09元。這一分的差距來自何時取位數,換個型別也不會替我們決定規則。
畫面還可能因地區而使用不同的小數點、千分位分隔符號與貨幣符號位置;幣別也不一定都顯示兩位小數。同一筆金額可以有不同的顯示文字,計算所依據的數值與幣別卻沒有因此改變。
空值:畫面有值,不代表使用者選過
以新建訂單的配送方式為例,如果下拉選單只有「宅配」「超商取貨」等實際選項,沒有「請選擇」,畫面一打開就可能顯示第一項。使用者沒碰這個欄位,送出時卻已帶上「宅配」;後端收到有效選項,也無法從這個值看出使用者是否真的選過。數字欄位也一樣:沒填商品數量,與明確填入0,是兩件事;直接補0,後續就無法判斷使用者是忘了填,還是確實輸入了0。
草稿也許可以暫時留白,確認訂單時則可能要求配送方式必填。如果這一項需要使用者決定,畫面保留「未選」狀態,送出時才能看出他是否做了選擇。預先選好第一項,即使欄位標示必填,系統收到的也只是個有效值。當業務本來就允許預設選項,預選才有了不同的意思。
修改既有資料時,還有另一種差別:請求裡沒有配送備註欄位,可能表示保留原值;送出 null 或空字串 "",可能表示清除,也可能依規則被拒絕。若API把這幾種情況都當成空白,原本的備註就可能在一次局部修改後消失,這些都是可能會發生的情況。
分清資料與顯示方式
昨天我們談過快取讓資料「讀得快」,今天我們看到資料需要「存得準」。在高併發或分散式架構下,如果時間基準混亂、金額計算因型別失準、或是空值語意不明,這些微小的誤差會在大量請求的放大下演變成嚴重的資料傾斜或商譽損失。釐清資料本質與顯示表現的邊界,系統才能在複雜的業務中,維持不變的準確與可靠。
無論前端與業務的顯示多麼多變,資料庫始終要用同一個標準來處理與儲存資料。
台灣有個電商平台在加入購物車的時候
預設是廠商宅配
我每次都忘記改成超商自取
應該有人知道我說哪間吧
![]()