雖然 Java 8+ 引入 java.time 新時間 API,但大量存量系統仍使用 java.util.Date 與 java.util.Calendar。本文匯總兩類核心操作:互轉、格式化、欄位存取、日期運算、星期判斷,並標註常見坑點與遷移建議。
| 類別 | 套件 | 特性 | 狀態 |
|---|---|---|---|
| Date | java.util | 毫秒時間戳、可變、非執行緒安全 | 舊版,大部分方法已棄用 |
| Calendar | java.util | 抽象基類,提供欄位運算、Locale 敏感 | 舊版,設計缺陷多 |
| SimpleDateFormat | java.text | 格式化/解析,**非執行緒安全** | 舊版,慎用於多執行緒 |
| LocalDateTime 等 | java.time | 不可變、執行緒安全、ISO-8601、功能完整 | **推薦新專案使用** |
Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// Date → String
String str = sdf.format(now); // "2024-03-15 14:30:45"
// String → Date
Date parsed = sdf.parse("2024-03-15 14:30:45");
常用格式符號:
| y | 年 | yyyy=2024, yy=24 |
| M | 月 | MM=03, MMM=Mar, MMMM=March |
| d | 日 | dd=05 |
| H | 時 (0-23) | HH=14 |
| h | 時 (1-12) | hh=02 |
| m | 分 | mm=05 |
| s | 秒 | ss=07 |
| S | 毫秒 | SSS=123 |
| E | 星期 | EEE=Mon, EEEE=Monday |
| Z | 時區偏移 | +0800 |
// Date → Calendar
Calendar cal = Calendar.getInstance();
cal.setTime(date);
// Calendar → Date
Date date = cal.getTime();
Calendar cal = Calendar.getInstance(); // 當前時間
// cal.set(2024, Calendar.MARCH, 15); // 月份 0-based: JAN=0
// 獲取欄位
int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH) + 1; // +1 顯示給人看
int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
int hour = cal.get(Calendar.HOUR_OF_DAY); // 24制
int minute = cal.get(Calendar.MINUTE);
int second = cal.get(Calendar.SECOND);
int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK); // SUN=1 ... SAT=7
int weekOfYear = cal.get(Calendar.WEEK_OF_YEAR);
int weekOfMonth= cal.get(Calendar.WEEK_OF_MONTH);
// 日期加減(自動處理進位/借位、閏年)
cal.add(Calendar.YEAR, 1); // +1 年
cal.add(Calendar.MONTH, -3); // -3 月
cal.add(Calendar.DAY_OF_MONTH, 7); // +7 天
cal.add(Calendar.HOUR, -2); // -2 小時
// 設定欄位(不自動重算其他欄位,慎用)
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0); // 當天 00:00:00.000
Calendar cal = Calendar.getInstance();
cal.set(2024, Calendar.MARCH, 15); // 2024-03-15
String[] weeks = {"", "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
System.out.println(weeks[cal.get(Calendar.DAY_OF_WEEK)]); // 星期五
Calendar cal = Calendar.getInstance();
// 本月第一天
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
Date monthStart = cal.getTime();
// 本月最後一天
cal.add(Calendar.MONTH, 1);
cal.add(Calendar.DAY_OF_MONTH, -1);
Date monthEnd = cal.getTime();
long diffMs = date2.getTime() - date1.getTime();
long diffDays = diffMs / (1000 * 60 * 60 * 24);
// 注意:夏令時切換日可能非 24h,精確場景請用 java.time
Calendar.JANUARY = 0,Calendar.DECEMBER = 11。設定/讀取月份必須 +1 或 -1。ThreadLocal 或每次新建實例,或改用 DateTimeFormatter (java.time)。GregorianCalendar,不可直接轉型為其他曆法。cal.set(MONTH, 11); cal.set(DAY_OF_MONTH, 31) 在 30 天月份會自動滾到下個月 1 日,建議用 add() 或 roll()。toString() 使用預設時區顯示,跨時區傳輸請用 ISO-8601 字串或時間戳。| 舊 API | 新 API (java.time) | 說明 |
|---|---|---|
| new Date() | Instant.now() / LocalDateTime.now() | 時間戳 / 本地日期時間 |
| Calendar.getInstance() | LocalDateTime.now() / ZonedDateTime.now() | |
| cal.get(YEAR) | ldt.getYear() | |
| cal.add(DAY, 7) | ldt.plusDays(7) | 不可變,返回新物件 |
| sdf.format(date) | dtf.format(ldt) | DateTimeFormatter 執行緒安全 |
| sdf.parse(str) | LocalDateTime.parse(str, dtf) | |
| Date.getTime() | instant.toEpochMilli() |
維護舊系統時熟練掌握 Calendar.add()、月份 0-based、SimpleDateFormat 執行緒安全問題;新專案**直接使用 java.time**,享受不可變、執行緒安全、功能豐富的現代 API。