時區、語系與時間寫法設定來源
目的
整理時區、時間寫入方式、資料表時間欄位、語系、多語系 path、translation 相關設定來源。
這類設定很容易造成新舊 API response、日期顯示、預約時間文字、notification template 的差異。
時間規則先集中放在這份文件。若後續內容膨脹,再拆成獨立的 時間設定.md,並從這裡連過去。
pro360_api_82
主要來源:
Lib/Common/Configuration.php
docker/staging/Configuration.php
Lib/Common/View.php
Lib/Common/Controller.php
Lib/Common/RouterV3.php常見 key 名稱:
date_default_timezone_set("UTC")
local_timezone
default_lang
use_multi_langs
use_multi_translation
seo_page_force_lang相關行為:
Configuration.php/ staging 覆蓋檔目前會把 PHP runtime timezone 設成 UTC。local_timezone目前是台北時間,常用於本地日期、使用者輸入時間、顯示或部分業務規則。View.php會處理 timezone display 與 translation lookup。Controller.php會解析 URL path 的 language segment。RouterV3.php會處理多語系 path routing。
get-lancer-php56
主要來源:
app/Config/core.php
app/Config/config.php
app/Config/settings.yml常見設定:
date_default_timezone_set(...)
Configure::write('Config.timezone', ...)
site.language
user.is_allow_user_to_switch_language目前掃描到 legacy core.php 使用 UTC;部分 quote 流程會明確使用 Asia/Taipei 解析使用者輸入或報表時間,再轉成 UTC 查詢 / 寫入。migration 時要以該 API / model 的實際寫法為準,不要只用全域設定推論。
時間來源分類
排查或移植時間相關邏輯時,先判斷目前流程用哪一種時間來源。
| 類型 | 常見寫法 | 意義 | 排查重點 |
|---|---|---|---|
| PHP runtime time | date('Y-m-d H:i:s'), time(), new DateTime() | 依目前 PHP default timezone 產生時間 | new API / legacy 多數 runtime 是 UTC,但可能被局部 DateTimeZone 覆蓋 |
| local timezone time | new DateTime('now', new DateTimeZone(local_timezone)), Asia/Taipei | 以台北時間做日期邊界、使用者輸入、顯示或業務日判斷 | 不要直接和 UTC DB 欄位做日期字串比較 |
| DB time | NOW(), CURRENT_TIMESTAMP, SYSDATE() | 由 DB server 產生時間 | 時間窗 SQL 要跟原本一樣用 DB time,避免 PHP / DB clock 差異 |
| 使用者輸入時間 | begin_date, end_date, 預約時間、工作時間 | 前端或 request body 傳入的業務時間 | 先看 legacy 是否以台北時間解析,再看儲存欄位是否轉 UTC |
| Redis TTL | setex, incrementKey, short TTL lock | 過期時間由 Redis TTL 決定 | 不一定有 DB 時間欄位可查,要看 key TTL 與程式設定 |
| queue / log time | event_queue.created, event_queue_log_*, Monolog table | 非同步事件建立 / 搬移 / 寫 log 的時間 | queue 可能已被 worker 搬到 log,查詢時要接受 queue 或 log 任一存在 |
資料表時間欄位規則
不要只看欄位名稱就推論時區或語意。先查該欄位在流程中怎麼寫入,再看它用來判斷什麼。
常見欄位語意:
| 欄位型態 | 常見例子 | 語意 |
|---|---|---|
| lifecycle time | created, modified | row 建立 / 更新時間;常用於時間窗、排序、最近一次異動 |
| event time | *_on, *_at, *_time | 某個業務事件發生時間,例如聯絡、付款、讀取、通話 |
| business date | *_date, begin_date, end_date | 使用者指定或業務定義的日期 / 區間,不一定等同系統寫入時間 |
| expiry time | expired_date, expires, end_time | 有效期限或結束時間,常牽涉 UTC / local day boundary |
| payment time | real_pay_date, paid_time | 付款完成或實際入帳判斷;不能只看 transaction created |
created / modified
- 代表資料列生命週期,不一定代表業務事件完成。
- migration 時要確認 legacy 是用 CakePHP 自動 timestamp、
date('Y-m-d H:i:s')、NOW()還是CURRENT_TIMESTAMP。 - 如果原 SQL 用
NOW()做created > DATE_SUB(NOW(), INTERVAL 24 HOUR),新實作應維持同一種 DB time 判斷,除非有明確理由改成 PHP time。 - 測試資料若手動改
created,要注意會影響 12 小時、24 小時、30 天等限制。
*_on / *_at / *_time
- 通常代表業務事件時間,例如:
quote_bids.contact_provider_on:consumer 正式聯絡 provider 的時間。quote_bids.quote_sent_on:provider quote / accept 成立時間。transactions.real_pay_date:實際付款完成時間。
- 這類欄位要和狀態欄位一起判讀;有時間不一定代表整個下游通知完成。
real_pay_date有值通常才代表 transaction 已付款;transactions.created只代表交易 row 建立。
begin_date / end_date / 預約時間
- 這類通常來自使用者輸入,先看 API body / form field 的格式。
- 若 legacy 以
Asia/Taipei建立DateTime再轉 UTC,新 API 要照同一規則。 - 若 legacy 直接保存傳入字串,新 API 不要自行轉 timezone,避免預約時間偏移。
- 文件或測試要標明「輸入時間」、「DB 儲存時間」、「顯示時間」三者是否相同。
queue / log 時間
event_queue.created代表事件 enqueue 時間,不代表 worker 已送出 notification。event_queue可能被搬到event_queue_log_1..event_queue_log_4。task_event_queue可能被搬到task_event_queue_log。- 排查 downstream event 時,不要只查原 queue table;要同時查 log table。
Migration 檢查清單
移植有時間欄位或時間窗的 API 時,文件至少要補這些資訊:
| 問題 | 要確認的內容 |
|---|---|
| 現在時間來源是什麼? | PHP date() / time()、DB NOW()、CURRENT_TIMESTAMP、Redis TTL |
| 使用哪個 timezone? | UTC、local_timezone、寫死 Asia/Taipei、使用者 timezone |
| 欄位語意是什麼? | row lifecycle、事件發生、付款完成、使用者預約、有效期限 |
| 是否用於限制? | 12 小時、24 小時、30 天、每日重置、短 TTL |
| 新舊是否一致? | legacy controller/model 的寫入方式與 new API 是否相同 |
| 測試如何避免誤判? | 使用固定 baseline、放寬秒級時間窗、不要跨日邊界跑脆弱 assertion |
排查 SQL 原則
- 查 schema 後再查資料,不要猜欄位型態。
- 查時間窗時,用和程式相同的時間來源:
- 程式用
NOW(),SQL 排查也用NOW()。 - 程式用 PHP 產生時間,排查時要記錄 PHP runtime timezone。
- 程式以台北日期做區間,查 DB 時要先轉成實際儲存 timezone 的起訖時間。
- 程式用
- 不要把 UI 顯示的台北時間直接拿去比 DB raw datetime,除非已確認該欄位就是用台北時間存。
- 有 queue / worker 的時間,至少保留數分鐘容忍區間,避免 worker timing 造成誤判。
排查原則
- 先分清楚「DB 寫入時間」、「業務事件時間」、「畫面顯示時間」。
- 日期寫入 DB 和畫面顯示要分開看。
- notification / SMS / email 文字可能還會依 user language 或 template 設定變化。
- legacy
settings.yml的語系不一定等同 new API 的default_lang。 - 涉及預約時間文字時,要同時看 requestor 語系、timezone display helper、reserve table。
- 涉及時間窗限制時,要寫明使用 DB
created、local timezone、PHP runtime time 還是 Redis TTL。