Windows XP 自動更新安裝時區補丁 (KB931836 等) 後,註冊表 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones 結構變更,導致 JVM 讀取預設時區失效,System.currentTimeMillis() 返回的時間戳雖正確,但 Calendar.getInstance()、Date 格式化輸出**顯示偏移 8 小時** (UTC+8 變為 UTC)。本文給出三種修復方案並分析優劣。
> java -version
java version "1.6.0_12"
> jshell
jshell> new java.util.Date()
$1 ==> Thu Jan 13 07:32:42 CST 2025 // 實際系統時間 15:32:42,偏移 -8h
GetTimeZoneInformation 獲取時區規則。TZI 二進制結構大小變化),導致舊版 JVM (≤ 1.6.0_13) 解析失敗,退回到 **GMT (UTC)**。Time Zones 節點 128 KB,更新後 72 KB,關鍵鍵值缺失/格式變更。| 方案 | 操作 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|---|
| 1. JVM 參數強制時區 | java -Duser.timezone=Asia/Shanghai -jar app.jar或代碼首行 TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai")); | 無需修改系統、立即生效、不影響其他程式 | 需修改啟動腳本/代碼、多 JVM 實例需逐個配置 | 開發環境、獨立部署應用、無法修改系統的受限環境 |
| 2. 升級 JRE/JDK | 升級至 1.6.0_14+ / 1.7.0_05+ / 1.8.0+ (含修復時區解析邏輯) | 根治、無代碼侵入、受益所有 Java 程式 | 需重新部署、舊應用可能不相容、XP 官方支援已終止 | 可控的生產環境、長期維護專案 |
| 3. 還原註冊表 (風險最高) | 從未更新機器導出 Time Zones 節點 (.reg),導入故障機器,重啟 | 系統級修復、所有語言程式受益 | 註冊表操作風險大、可能破壞其他依賴新結構的程式、需重啟 | 無法升級 JVM、無法修改啟動參數的極端場景 |
@SpringBootApplication
public class App {
public static void main(String[] args) {
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai")); // 必須在 SpringContext 啟動前
SpringApplication.run(App.class, args);
}
}
jshell> java.util.TimeZone.getDefault().getID()
$2 ==> "Asia/Shanghai"
jshell> new java.util.Date()
$3 ==> Thu Jan 13 15:32:42 CST 2025 // 正確
eclipse-temurin:17-jre 內含完整 tzdata,避免宿主機時區污染。-Duser.timezone=UTC (或業務時區),日誌/資料庫/監控全鏈路 UTC,消除時區歧義。