如果你是一名 Java 開發者,很可能在日常工作中經常見到 java.util.Date
類。但你可能也注意到,現在 Java 開發中,我們通常會避免直接使用它。為什麼會這樣呢?今天我們就來聊聊這個話題。
java.util.Date
自 Java 1.0 (1996年) 就存在了,可以說它是 Java 標準庫中的「元老」了。但正如許多古老的事物一樣,它帶著當時設計的局限性:
Date date = new Date();
System.out.println(date);
(控制台輸出結果)
Mon Sep 22 09:50:24 CST 2025
看起來很簡單對吧?但問題就藏在這簡單背後。
Date
類的許多方法都已經過時(deprecated),而且設計上存在很多不合理之處:
Date date = new Date();
System.out.println("當前年月日:" + LocalDate.now());
System.out.println(date.getYear());
System.out.println(date.getMonth());
(控制台輸出結果)
當前年月日:2025-09-22
125
8
看到問題了嗎?年份從 1900 年開始計算,月份從 0 開始(0 表示一月,11 表示十二月)。這種反直覺的設計很容易導致錯誤。
Date
對象是可變的(mutable),這意味著你創建了一個 Date 對象後,它的值還可以被改變:
Date date = new Date(2025 - 1900, 8, 22);
System.out.println("原定日期: " + date);
// 某人意外地修改了這個日期的年份
date.setYear(2026 - 1900);
System.out.println("修改後的日期: " + date);
(控制台輸出結果)
原定日期: Mon Sep 22 00:00:00 CST 2025
修改後的日期: Tue Sep 22 00:00:00 CST 2026
這種可變性在多線程環境下尤其危險,容易導致難以調試的並發問題。
Date
實際上並不存儲時區資訊,它只是自 1970年1月1日00:00:00 GMT 以來的毫秒數。但它的 toString()
方法卻使用系統默認時區來顯示時間,這很容易造成混淆:
Date now = new Date();
System.out.println(now); // 輸出取決於你的默認時區
Date
只能精確到毫秒級別,對於需要更高精度(如微秒、納秒)的應用場景無法滿足需求。
假設我們要計算兩個日期之間的天數差,使用 Date
會非常麻煩:
// 使用 Date 計算兩個日期相差的天數(不推薦的方式)
Date date1 = new Date(125, 8, 22); // 2025年9月22日
Date date2 = new Date(125, 9, 22); // 2025年10月22日
long difference = date2.getTime() - date1.getTime();
long daysBetween = difference / (1000 * 60 * 60 * 24);
System.out.println("相差天數: " + daysBetween);
(控制台輸出結果)
相差天數: 30
這段代碼不僅難以閱讀,還需要手動處理毫秒轉換,容易出錯。
自從 Java 8 (2014年) 引入了 java.time
包,我們有了現代、完善的時間日期處理 API。
// 創建指定日期
LocalDate date = LocalDate.of(2025, 9, 22); // 2025年9月22日,直觀明瞭!
System.out.println(date);
// 獲取當前日期時間
LocalDateTime now = LocalDateTime.now();
System.out.println(now);
// 獲取當前日期時間,帶時區
ZonedDateTime zonedNow = ZonedDateTime.now(ZoneId.of("Asia/Taipei"));
System.out.println(zonedNow);
(控制台輸出結果)
2025-09-22
2025-09-22T10:30:21.743
2025-09-22T10:30:21.744+08:00[Asia/Taipei]
LocalDate appointment = LocalDate.of(2025, 9, 22);
// 下面的操作會返回新的對象,原對象不變
LocalDate newDate = appointment.plusDays(30);
// 計算兩個日期之間的天數
LocalDate date1 = LocalDate.of(2025, 9, 22);
LocalDate date2 = LocalDate.of(2025, 10, 22);
long daysBetween = ChronoUnit.DAYS.between(date1, date2);
System.out.println("相差天數: " + daysBetween);
(控制台輸出結果)
相差天數: 30
// 明確處理時區
ZonedDateTime taipeiTime = ZonedDateTime.now(ZoneId.of("Asia/Taipei"));
ZonedDateTime newYorkTime = taipeiTime.withZoneSameInstant(ZoneId.of("America/New_York"));
通常不建議,請慎重選擇。
如果你在維護老項目,看到大量的 Date
代碼,可以考慮以下遷移策略:
java.time
包中的類/**
* 將 java.util.Date 轉換為 LocalDate
* @param date java.util.Date
* @return LocalDate(僅包含年月日),如果 date 為 null 返回 null
*/
public static LocalDate toLocalDate(Date date) {
if (Objects.isNull(date)) {
return null;
}
return date.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate();
}
/**
* 將 java.util.Date 轉換為 LocalDateTime
* @param date java.util.Date
* @return LocalDateTime(包含年月日 + 時分秒),如果 date 為 null 返回 null
*/
public static LocalDateTime toLocalDateTime(Date date) {
if (Objects.isNull(date)) {
return null;
}
return date.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDateTime();
}
java.util.Date
就像是編程世界中的「古董」——它有歷史價值,但在現代開發中有更優的 API 可以替換。它的設計缺陷、線程安全問題以及難以使用的 API 都讓我們有充分的理由轉向更現代的 java.time
API。
希望這篇文章能幫助你理解為什麼不推薦使用 java.util.Date
,以及如何在你的項目中使用更好的替代方案。