先定義自然日與營運日
日報的「一天」可能是自然日00:00到24:00,也可能是工廠營運日或班次。本文用Asia/Taipei的虛構夜班22:00到次日06:00說明。先保存時區、班別版本、start與end,再把sourceTimestamp轉成該時區比較;不要以檔案收到時間決定歸屬。Asia/Taipei當代沒有夏令時間,但系統若支援其他地區仍需使用時區資料庫,不能只存固定+08。
| 報表類型 | 起訖定義 | 跨午夜 | 用途 |
|---|---|---|---|
| 自然日 | 00:00–24:00 | 不跨自身邊界 | 日曆統計 |
| 夜班 | 22:00–06:00 | 跨次日 | 交班產量 |
| 營運日 | 企業規則 | 依版本 | 工廠管理 |
| 事件窗 | 事件前後固定分鐘 | 可能跨日 | 異常調查 |
班次表要保存shiftId與版本。例如N-2026-09-17代表本地日期17日22:00開始、18日06:00結束。資料查詢用半開區間[start,end),22:00樣本歸當班,06:00樣本歸下一個班,避免邊界重複。
警報與產量可有不同歸屬規則,但同一報表要說清楚。警報依sourceTimestamp,人工註解依operatorAt,累計讀值依取樣時間,不能全部使用收到時間。
若系統支援跨廠時區,跨日測試另納入夏令時間地區。用固定時區的案例檢查程式是否硬編碼+08,轉換到23或25小時日的zone時才能發現錯誤。
報表查詢輸入應把時間區間顯示給使用者:本地起訖、UTC起訖、時區名稱、半開或閉區間。這能避免一個人選22:00到06:00、另一個人選到05:59,卻以為兩者結果應完全相同。跨午夜的自然日與班日數字同時提供,交班時可比較差異來源。
營運日名稱可以含開始日期而不是結束日期,例如N-17表示17日夜班。名稱規則寫入班表版本,避免同一組資料在不同報表被叫成N-18。
22到06的逐筆歸屬
虛構讀值:17日21:59產量980,22:00為981,23:30為995,18日00:10警報E1,05:59產量1005,06:00為1006。夜班N的事件列按22:00含至06:00不含篩選,累計量差分則另取兩個邊界讀值;21:59歸前班,06:00歸下一班。累計差分先確認邊界讀值、排序與去重,並排除中途reset或回繞;不能把事件列篩選直接當差分所需資料集合。
| 本地時間 | 資料 | N夜班歸屬 | 自然日 |
|---|---|---|---|
| 17 21:59 | value=980 | 否 | 17日 |
| 17 22:00 | value=981 | 是 | 17日 |
| 17 23:30 | value=995 | 是 | 17日 |
| 18 00:10 | alarm E1 | 是 | 18日 |
| 18 05:59 | value=1005 | 是 | 18日 |
| 18 06:00 | value=1006 | 否 | 18日 |
警報E1的日期欄是18日,但班別欄是N-17;報表要同時保留兩個欄位,不能為了顯示夜班而改寫sourceTimestamp。跨日查詢若只用日期欄,會把夜班拆開;若只用班別欄,又會失去自然日的稽核意義。
若設備累計值定義為截至該瞬間前的總量,且22:00與06:00邊界讀值有效、同一計數世代、無reset或回繞,整班可算1006-981=25。06:00這筆雖不屬夜班事件列,仍是夜班差分的結束基準。若只有05:59的1005,則只能確認至05:59增加24,最後一分鐘未知;不能以24假稱完整班產量。
資料缺口不能由零填補。零是設備明確報零,缺值是沒有觀測;日報應分開計算有效筆數、缺測時間與可支持產量。
排錯時用一筆22:00、00:00、06:00資料追蹤SQL或平台查詢的含端規則,確認start含、end不含。若重跑筆數變化,保存查詢版本與時區設定。
日報審核不只看總量,也要看邊界筆數和品質。將22:00與06:00附近資料單列,能很快發現半開區間寫反或時區轉換重複計數。
時區與DST排錯
固定UTC儲存、顯示時轉Asia/Taipei是常見設計,但查詢層仍要明確傳入時區。若資料帶Z,先轉換再取日期;若只有無時區字串,不能猜它是UTC或台北。Asia/Taipei當代沒有DST,所以一天仍為24小時;換成America/New_York等地區時,DST切換日可能是23或25小時,班次長度與日平均分母要依當地時區資料庫計算。
| 輸入 | 轉換 | 日期/班別 | 排錯 |
|---|---|---|---|
| 2026-09-17T14:00Z | 台北22:00 | N-17開始 | 確認Z |
| 2026-09-17T22:00Z | 台北18日06:00 | 下一班邊界 | 半開區間 |
| 無offset字串 | 未知 | 不可歸屬 | 補來源時區 |
| DST地區凌晨 | 依zone規則 | 23或25小時 | 查tzdata版本 |
不要用固定加八小時處理所有報表;這只適合已確認Asia/Taipei且資料沒有歷史時區變更的情況。排錯先查來源timestamp型別、資料庫時區、應用程式zone、班表版本和夏令時間資料庫版本,再比較一筆跨午夜樣本。
班別邊界不能靠畫面顏色表示,資料層要有可查的shiftId與scheduleVersion。若人員在月中修改班表,舊資料仍按舊版本查詢,新資料按新版本建立,否則同一天會被兩套規則重算。
班報摘要若跨午夜,標題同時顯示本地開始日期、結束日期、時區與班別名稱。只寫「夜班」容易在資料匯出或跨廠比較時混淆。
DST地區的班次可能不是八小時,日報不可固定除以28800秒。先將start與end轉為zone後的實際instant,報表標示本班實際時長;Asia/Taipei案例則維持24小時自然日。
交付驗收與限制
驗收建立六筆資料,逐筆填sourceTimestamp、displayLocal、calendarDate、shiftId、是否邊界與品質。成功條件是21:59、22:00、05:59、06:00分別落到預期集合;故意把時區改成UTC,應看到日期與班別結果改變並留下查詢設定。
資料補送依sourceTimestamp歸屬,receivedAt只作延遲分析。若班表後來改為21:00開始,新增shiftScheduleVersion,不覆寫舊報表。缺少時間戳或時區的資料列保留Unknown,不能用伺服器當地時間補成精確數量。
本文以Asia/Taipei離線案例說明,實作前要核對平台時間戳、時區轉換、排程、DST與資料庫函數版本。
完成前讓另一位值班者依班報讀出四個邊界樣本,若不需口頭補充就能判斷歸屬,才算畫面與資料契約清楚。
若資料平台把無時區timestamp當伺服器時間,部署到另一個Gateway可能整班偏移。部署檢查要用固定UTC案例和跨午夜案例,查資料庫連線、應用層、瀏覽器與匯出檔四個時區。
時間邊界測試須包含前一分鐘、正好起點、正好終點與下一分鐘,並把每筆歸屬寫出。這比只測一個一般日更容易找到半開區間和跨午夜錯誤。
若平台將夏令時間轉換成重複的本地小時,需以UTC instant或fold標記區分兩筆同名時間;Asia/Taipei案例沒有此問題,但跨廠報表不能省略這個規則。
班別資料若由多個收集器合併,先統一時區與sourceTimestamp,再套班表;不同來源的receivedAt不能直接拿來排產量。
交付給管理者的摘要可同時顯示自然日總量、班次總量與邊界未知量,避免用一個數字掩蓋日期規則。任何因時區或班表變更產生的差異,都附版本與重算時間。
FAQ與來源
FAQ1:夜班跨午夜是否要拆成兩天?自然日可以拆,班報應以shiftId保留完整班次。
FAQ2:06:00樣本屬上一班嗎?本文半開區間規則下屬下一班,專案可另定但要固定。
FAQ3:Asia/Taipei要套DST嗎?當代不套,但其他zone可能有23或25小時日。
FAQ4:無時區字串能直接當台北時間嗎?不能,先補來源契約或標未知。
時間戳精度也要保留。來源只有分鐘時,22:00:30不應假裝成精確事件;在邊界附近可標示Uncertain,等來源補充秒級資料。
練習把UTC 17日14:00與22:00分別換成台北17日22:00與18日06:00,夜班實際八小時。另在支援DST的測試站點依該版時區資料建立春秋轉換日;不能把台北的一日秒數直接複製成全球固定規則。