在 Java 開發中,時間獲取、格式化與運算是高頻需求。早期版本主要依賴 java.util.Date、java.util.Calendar 與 java.text.SimpleDateFormat,雖然 Java 8 引入了全新的 java.time 套件,但舊版 API 在大量存量專案中仍廣泛使用。本文系統整理 Calendar 與 SimpleDateFormat 的常見操作模式,並給出生產級代碼範例。
Calendar.getInstance() 返回預設時區(如 CST)的日曆實例,直接呼叫 getTime() 可得到 Date 物件:
Calendar cal = Calendar.getInstance();
Date now = cal.getTime();
// 輸出範例:Fri Jan 06 21:03:36 CST 2017
常用欄位常數:
Calendar.YEAR:年份(如 2017)Calendar.MONTH:月份(0-11,需 +1)Calendar.DAY_OF_MONTH:日期Calendar.HOUR_OF_DAY:24 小制小時Calendar.MINUTE、Calendar.SECOND:分、秒注意:get() 返回皆為 int 型別。
線程不安全,建議每次使用新建實例或配合 ThreadLocal:
DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(fmt.format(new Date()));
// 輸出:2017-01-06 21:19:02
常用格式字元:
| 字元 | 含義 | 範例 |
|---|---|---|
| yyyy | 年份 | 2017 |
| MM | 月份 | 01 |
| dd | 日期 | 06 |
| HH | 小時(0-23) | 21 |
| mm | 分鐘 | 19 |
| ss | 秒數 | 02 |
錯誤示範:直接對 get() 結果加減,不會改變 Calendar 內部狀態,且無法處理進位(如 23:59 + 2 分鐘)。
正確做法:使用 add(field, amount),自動處理月份翻年、小時跨日等邊界:
// 一小時後
cal.add(Calendar.HOUR_OF_DAY, 1);
// 6 分鐘前
cal.add(Calendar.MINUTE, -6);
若需保留原時間點,先 clone() 或重新 getInstance()。
Calendar cal1 = Calendar.getInstance();
Calendar cal2 = Calendar.getInstance();
cal2.add(Calendar.HOUR, 7); // cal2 比 cal1 晚 7 小時
// 比較先後:cal1 < cal2 返回 -1
int cmp = cal1.compareTo(cal2);
// 計算小時差(毫秒換算)
long diffMs = cal2.getTimeInMillis() - cal1.getTimeInMillis();
long diffHours = diffMs / (1000 * 60 * 60); // 輸出 7
避坑提醒:getTimeInMillis() 返回 UTC 毫秒數,跨時區計算差值時無需額外處理時區偏移。
LocalDateTime、DateTimeFormatter、Duration/Period 線程安全且 API 更直觀。SimpleDateFormat 封裝為線程安全的 DateUtils,統一格式常數。yyyy-MM-dd'T'HH:mm:ss.SSSXXX,避免時區歧義。掌握上述核心操作,即可應對 90% 以上的業務時間處理場景。