首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java 時間處理權威指南:Date、Calendar、DateFormat 核心用法全解析

     編輯:Java 程式設計入門

Java 標準庫中 java.util.Date、java.util.Calendar、java.text.DateFormat 組成了時間處理的「鐵三角」。雖然 Java 8 引入了全新的 java.time 包,但存量系統中這三大類仍佔據主導地位。本文將系統講解它們的類層次、核心方法、格式化解析、日期運算、時區處理,並對比 java.sql.Date 差異,助你構建完整時間處理知識體系。

一、類層次與設計演進

類/接口類型核心職責關鍵說明
java.util.Date具體類時間戳容器(毫秒精度)JDK 1.0 引入,早期包含大量棄用方法(getYear/setMonth 等),現僅作毫秒數承載
java.text.DateFormat抽象類日期/時間格式化與解析規範提供靜態工廠方法獲取本地化格式化器
java.text.SimpleDateFormat具體類模式字符串驅動的格式化實現非線程安全,支援自定義模式(yyyy-MM-dd HH:mm:ss 等)
java.util.Calendar抽象類日曆字段運算與時區感知替代 Date 棄用方法,支援加減運算、字段獲取/設置
java.util.GregorianCalendar具體類公曆(格里高利曆)實現Calendar 唯一公共具體子類,處理閏年、時代(BC/AD)

二、Date:毫秒時間戳的獲取與轉換

// 1. 獲取系統當前時間戳
Date now = new Date();                    // 等價 new Date(System.currentTimeMillis())
long timestamp = now.getTime();           // 1970-01-01 00:00:00 UTC 起的毫秒數
long timestamp2 = System.currentTimeMillis(); // 更高效,避免創建對象

// 2. 從毫秒數構造 Date
Date specific = new Date(1_000_000_000_000L); // 2001-09-09 01:46:40 UTC

// 3. Date ↔ String(需配合格式化類)
String iso = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").format(now); // 2024-01-15T14:30:00+08:00

三、SimpleDateFormat:模式驅動的格式化與解析

3.1 常用模式字符速查

字母含義示例
y年yy=24, yyyy=2024
M月M=1, MM=01, MMM=Jan, MMMM=January
d月中的天d=5, dd=05
H小時(0-23)H=9, HH=09
m分鐘m=5, mm=05
s秒s=9, ss=09
S毫秒SSS=005
E星期E=Tue, EEEE=Tuesday
Z/X時區偏移Z=+0800, XXX=+08:00

3.2 格式化與解析實戰

SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

// 格式化 Date → String
String formatted = fmt.format(new Date()); // "2024-01-15 14:30:00"

// 解析 String → Date(必須捕獲 ParseException)
try {
    Date parsed = fmt.parse("2024-01-15 14:30:00");
    System.out.println(parsed.getTime());
} catch (ParseException e) {
    throw new IllegalArgumentException("日期格式錯誤", e);
}

// 線程安全最佳實踐:ThreadLocal 緩存或每次新建
ThreadLocal TL_FMT = ThreadLocal.withInitial(
    () -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

四、DateFormat 靜態工廠:本地化預定義風格

Date now = new Date();
DateFormat shortFmt  = DateFormat.getDateTimeInstance(DateFormat.SHORT,  DateFormat.SHORT);
DateFormat mediumFmt = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
DateFormat longFmt   = DateFormat.getDateTimeInstance(DateFormat.LONG,   DateFormat.LONG);
DateFormat fullFmt   = DateFormat.getDateTimeInstance(DateFormat.FULL,   DateFormat.FULL);

// 輸出示例(Locale.US)
// SHORT:  1/15/24, 2:30 PM
// MEDIUM: Jan 15, 2024, 2:30:00 PM
// LONG:   January 15, 2024, 2:30:00 PM CST
// FULL:   Monday, January 15, 2024, 2:30:00 PM China Standard Time

兩個參數分別控制「日期風格」與「時間風格」,均為 int 常量:SHORT, MEDIUM, LONG, FULL、DEFAULT(=MEDIUM)。

五、Calendar:字段級日期運算核心

5.1 核心字段常量

YEAR, MONTH(0-11), DAY_OF_MONTH, DAY_OF_WEEK, DAY_OF_YEAR,
WEEK_OF_MONTH, WEEK_OF_YEAR,
HOUR(0-11), HOUR_OF_DAY(0-23), MINUTE, SECOND, MILLISECOND,
ZONE_OFFSET, DST_OFFSET, ERA(BC/AD)

5.2 典型操作模式

GregorianCalendar cal = new GregorianCalendar(); // 默認時區、Locale
cal.setTime(new Date()); // 同步系統時間

// 1. 獲取字段
int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH) + 1; // 0-based → 1-based
int day = cal.get(Calendar.DAY_OF_MONTH);

// 2. 設置字段(會重新計算其他字段)
cal.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY); // 調整到本週五
cal.set(2024, Calendar.DECEMBER, 25); // 直接設置年月日

// 3. 加減運算(自動處理進位、閏年、月長短)
cal.add(Calendar.DAY_OF_MONTH, 7);   // 7 天后
cal.add(Calendar.MONTH, -3);         // 3 月前
cal.roll(Calendar.DAY_OF_MONTH, 10); // 僅滾動天,不改變月/年

// 4. 時區轉換
cal.setTimeZone(TimeZone.getTimeZone("UTC"));
Date utcTime = cal.getTime(); // 獲取對應 UTC 時刻的 Date

5.3 實戰:計算未來 N 個「星期五 13 號」

DateFormat full = DateFormat.getDateInstance(DateFormat.FULL);
GregorianCalendar cal = new GregorianCalendar();
cal.setTime(new Date());
cal.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY); // 對齊最近的週五

int found = 0;
while (found < 10) {
    cal.add(Calendar.DAY_OF_MONTH, 7); // 下一個週五
    if (cal.get(Calendar.DAY_OF_MONTH) == 13) {
        System.out.println(full.format(cal.getTime()));
        found++;
    }
}

六、java.sql.Date vs java.util.Date:持久層陷阱

特性java.util.Datejava.sql.Date
繼承關係無extends java.util.Date
精度毫秒僅保留日期部分(時分秒毫秒歸零)
用途通用業務邏輯JDBC 對應 SQL DATE 類型
構造new Date()new java.sql.Date(utilDate.getTime())
toString()Mon Jan 15 14:30:00 CST 20242024-01-15

最佳實踐: DAO 層入參/出參使用 java.time.LocalDate(Java 8+)或 java.sql.Date;Service/Controller 層統一使用 java.time.LocalDateTime 或 java.util.Date;轉換由持久層框架(MyBatis/Hibernate)自動處理,手動轉換僅在邊界必要時進行。

七、避坑指南與現代化建議

  1. SimpleDateFormat 非線程安全:不可作為 static 字段共享,使用 ThreadLocal、局部變量或 Java 8 DateTimeFormatter(不可變、線程安全)。
  2. Calendar.MONTH 0-based 陷阱:1月=0,12月=11,設置/獲取時必須 ±1。
  3. 時區隱式依賴:Date.toString()、SimpleDateFormat 默認使用 JVM 默認時區(TimeZone.getDefault()),分佈式系統務必顯式指定時區或統一 UTC 存儲。
  4. 棄用方法禁用:Date.getYear(), Date.setHours() 等編譯期即報警,嚴禁使用。
  5. 新項目首選 java.time:LocalDate, LocalDateTime, ZonedDateTime, Instant, Duration, Period, DateTimeFormatter — 不可變、線程安全、API 清晰、時區處理徹底。

八、總結

Date 承載時間戳,Calendar 操作日曆字段,DateFormat 處理人類可讀表達,三者分工明確、協作緊密。掌握其核心 API、常見陷阱、線程安全模式,是維護存量 Java 系統、平滑遷移至 java.time 的必修課。建議團隊建立「時間處理編碼規約」:統一 UTC 存儲、顯式時區傳遞、禁用棄用方法、新代碼強制 java.time,從源頭消除時間類 Bug。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved