iT邦幫忙

2025 iThome 鐵人賽

DAY 15
0
佛心分享-SideProject30

我的時間到底去哪裡了!? – 個人時間數據系統開發挑戰系列 第 15

Java 日期時間的三種常見型別比較:Date、LocalDateTime、Instant

  • 分享至 

  • xImage
  •  

前言

在前幾天的文章中,我們已經完成了 MyMomentum 專案的核心功能開發,包括活動管理、記錄追蹤等。今天想跟大家聊聊一個看似簡單但其實很重要的技術選擇:在 Java 中應該用什麼型別來儲存日期時間?

今天就舉我自己在開發維護的時候最常看到的三個日期時間型別為例,來比較一下:

Date、LocalDateTime 和 Instant。

Date:老舊但仍在使用的型別

雖然 Date 現在還有很多專案在使用,但它的設計已經不符合現代 Java 的開發需求。它本身只存毫秒數,但輸出時會依 JVM 時區顯示,常讓人誤以為內含時區,導致時區處理變得複雜。

Date now = new Date();
System.out.println(now); // 輸出:Mon Jan 15 10:30:00 CST 2024
  • 出現時間java.util.Date 是 Java 很早期(JDK 1.0)就有的日期類別。
  • 內部實作:其實就是儲存「自 1970-01-01 00:00:00 UTC 起算的毫秒數」。
  • 主要問題
    1. API 設計不直觀:像 getYear()getMonth() 這些方法回傳的值跟實際年份/月分有偏差(已標示為過時)。
    2. 沒有時區概念Date 本身只有「時間點」,沒有明確的時區資訊。
    3. 可變物件:修改後會直接影響原本的 Date,容易造成 bug。
  • 常見用途
    • 與舊系統整合(例如 JDBC、老舊的 API)。
    • 維護既有專案時不得不用。

LocalDateTime:本地時間的選擇

LocalDateTime 適合用在「不需要考慮時區」的場景,比如:

  • 生日記錄
  • 本地活動的時間安排
  • 不涉及跨時區的業務邏輯
LocalDateTime now = LocalDateTime.now();
System.out.println(now); // 輸出:2024-01-15T10:30:00
  • 出現時間java.time.LocalDateTime 是 Java 8 推出的 java.time(JSR-310)新日期時間 API 之一。
  • 內部實作:由「日期(年月日)」和「時間(時分秒)」組合而成,但不包含時區資訊
  • 主要特點
    1. 不可變物件:操作會產生新物件,避免意外修改。
    2. API 設計清晰:有 plusDays()minusHours()withMonth() 等直觀的方法。
    3. 適合業務邏輯:例如「2025-09-26 15:30」,不需要考慮時區。
  • 常見用途
    • 表示使用者輸入的日期時間(如活動時間、會議時間)。
    • 只關心「當地時間」而不是全域唯一的時間點。

但問題來了:如果你的應用要服務全球用戶,LocalDateTime 就會造成困擾。同樣的 2024-01-15T10:30:00,在台灣是上午 10:30,在美國東岸卻是前一天的晚上 9:30。

Instant:全球統一的時間點

java.time.Instant 代表的是「從 1970-01-01T00:00:00Z 開始的秒數」

Instant 最適合用在:

  • 需要跨時區的應用
  • 資料庫儲存
  • API 傳輸
  • 日誌記錄
Instant now = Instant.now();
System.out.println(now); // 輸出:2024-01-15T02:30:00Z
  • 出現時間java.time.Instant 也是 Java 8 推出的新 API。
  • 內部實作:和舊的 Date 類似,儲存「自 1970-01-01 00:00:00 UTC 起算的秒數 + 奈秒數」。
  • 主要特點
    1. 時區無關:永遠以 UTC 為基準
    • 精確到奈秒:比 Date 的毫秒更精確
    • 全球統一:同一個 Instant 在全球任何地方都代表同一個時間點
  • 常見用途
    • 系統紀錄(如資料建立時間、修改時間、事件觸發時間)。
    • 分散式系統中需要一致的時間標準(例如跨伺服器的時間同步)。
    • 適合用來存進資料庫,避免時區換算錯誤。

為什麼我選 Instant:MyMomentum 的實際考量

在設計 MyMomentum 的活動記錄功能時,我選擇了 Instant,原因如下:

1. 跨時區用戶支援

我想要假設MyMomentum是可以推到世界各地的產品 。如果我用 LocalDateTime 儲存「早上 8 點運動」,台灣用戶看到的是 8:00,但美國用戶看到的是前一天的 19:00,這會造成很大的混淆。

用 Instant 儲存後,每個用戶都能看到正確的本地時間:

// 儲存時
@Column(name = "executed_at")
private Instant executedAt;

// 前端顯示時轉換為用戶當地時間
Instant utcTime = record.getExecutedAt();
ZonedDateTime localTime = utcTime.atZone(userTimeZone);

2. 資料庫查詢的一致性

PostgreSQL 的 TIMESTAMPTZ 型別與 Instant 完美對應,查詢和排序都很直觀:

-- 查詢最近 7 天的記錄
SELECT * FROM activity_records 
WHERE executed_at >= NOW() - INTERVAL '7 days'
ORDER BY executed_at DESC;

3. API 設計的標準化

REST API 使用 ISO 8601 格式傳輸時間,Instant 天然支援:

{
  "executedAt": "2024-01-15T02:30:00Z",
  "duration": 3600
}

4. 避免時區轉換錯誤

使用 LocalDateTime 時,開發者很容易忘記時區轉換,導致資料不一致。Instant 強制我們在儲存時就考慮時區問題,減少了這類 bug。

總結:如何選擇適合的日期時間型別

經過比較,大概可以這樣看:

型別 適用場景 不適用場景
Date 維護舊專案 新專案開發
LocalDateTime 純本地業務、生日記錄 跨時區應用
Instant 跨時區應用、資料庫儲存、API 設計 純本地時間顯示

上一篇
前端 Activities CRUD — React + TypeScript 程式實作
系列文
我的時間到底去哪裡了!? – 個人時間數據系統開發挑戰15
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言