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

Java 日期時間核心:Calendar 架構原理與 API 深度解析

     編輯:Java 相關知識

前言

Java 中的日期時間處理涉及 Calendar、Date、DateFormat 三大核心類。本文系統梳理它們的架構關係、Calendar 的內部運作原理、17 個核心欄位定義,以及完整的 API 使用指南,助你建立堅實的時間處理知識體系。

一、Java 時間架構圖譜

Java Calendar, Date, DateFormat 關係圖

關鍵點:

  1. 毫秒基準:Calendar 與 Date 皆以「距 1970-01-01 00:00:00 GMT 的毫秒數」為時間基準。
  2. Calendar 抽象工廠:Calendar.getInstance() 根據 Locale 與 TimeZone 返回具體實現(預設 GregorianCalendar,日文環境返回 JapaneseImperialCalendar,泰文環境返回 BuddhistCalendar)。
  3. Date 定位:JDK 1.1 後僅作為毫秒數容器,格式化/解析交由 DateFormat(實際為 SimpleDateFormat)處理。

二、Calendar 核心原理:從毫秒到欄位的計算鏈路

1. 時間存儲與欄位陣列

protected long time;          // 毫秒時間戳
protected int fields[];       // 17 個欄位值陣列
protected boolean isSet[];    // 欄位是否已設定標記
protected int stamp[];        // 欄位設定時間戳(用於判斷臟資料)

2. 17 個核心欄位常量

常量值含義範例/範圍
ERA0紀元0=BC, 1=AD
YEAR1年2024
MONTH2月(0 起始)0=JANUARY ... 11=DECEMBER
WEEK_OF_YEAR3年間第幾週1-53
WEEK_OF_MONTH4月內第幾週1-6
DATE / DAY_OF_MONTH5月內第幾天1-31
DAY_OF_YEAR6年間第幾天1-366
DAY_OF_WEEK7星期幾1=SUNDAY ... 7=SATURDAY
DAY_OF_WEEK_IN_MONTH8月內第幾個該星期幾1-5
AM_PM9上午/下午0=AM, 1=PM
HOUR1012 小時制時0-11
HOUR_OF_DAY1124 小時制時0-23
MINUTE12分0-59
SECOND13秒0-59
MILLISECOND14毫秒0-999
ZONE_OFFSET15時區偏移毫秒-
DST_OFFSET16夏令時偏移毫秒-

3. get(int field) 執行流程

public int get(int field) {
    complete();                // 1. 觸發完整計算
    return internalGet(field); // 2. 直接讀取 fields[field]
}

protected void complete() {
    if (!isTimeSet) updateTime();           // 毫秒 → 欄位
    if (!areFieldsSet || !areAllFieldsSet) {
        computeFields();                    // 核心計算邏輯
        areAllFieldsSet = areFieldsSet = true;
    }
}

computeFields() 在 GregorianCalendar 中實現,將毫秒數拆解為年月日時分秒,處理閏年、格里曆切換、時區偏移等複雜邏輯。

三、Calendar 核心 API 分類詳解

1. 欄位範圍查詢

  • getMaximum(field):理論最大值(如 DATE 最大 31)
  • getActualMaximum(field):當前日期下的實際最大值(如 9 月 DATE 最大 30)
  • getMinimum(field) / getActualMinimum(field):同理最小值

2. 欄位讀寫與運算

方法用途關鍵差異
get(field)取得當前欄位值-
set(field, value)設定欄位值(延遲計算)不立即重算 time,標記 isSet
add(field, amount)增加/減少欄位值(聯動大欄位)月份 +1 可能導致年份 +1
roll(field, amount)循環滾動欄位值(不聯動大欄位)12 月 roll +1 → 1 月,年份不變
clear(field)重置欄位為最小值慎用,可能破壞日期一致性
isSet(field)判斷欄位是否被顯式設定-

3. 日期比較與寬容模式

  • 比較:equals()、before()、after()、compareTo()
  • 寬容模式:
    • setLenient(true):允許非法值(如 MONTH=12 自動進位),get() 時正規化
    • setLenient(false):嚴格檢查,get()/add()/roll() 時拋出 IllegalArgumentException

4. 時間轉換與時區

// Date 互轉
Date getTime();
void setTime(Date date);

// 毫秒互轉
long getTimeInMillis();
void setTimeInMillis(long millis);

// 時區
TimeZone getTimeZone();
void setTimeZone(TimeZone zone);

5. 週曆規則設定

  • getFirstDayOfWeek() / setFirstDayOfWeek():週起始日(美式 Sunday,法式 Monday)
  • getMinimalDaysInFirstWeek() / setMinimalDaysInFirstWeek():首週最少天數(影響 WEEK_OF_YEAR 計算)

四、完整測試範例:CalendarTest.java

原文提供的 CalendarTest 涵蓋所有 API 類別的驗證邏輯,核心測試方法包括:

  • testAllCalendarSections():遍歷 17 個欄位,驗證 min/max/actualMin/actualMin/set/roll/add 行為
  • testComparatorAPIs():驗證 equals/before/after/compareTo
  • testLenientAPIs():展示寬容/非寬容模式異常拋出時機
  • testTimeAPIs():Date/毫秒/時區互轉與多參數 set()
  • testOtherAPIs():clone、週曆規則對 WEEK_OF_YEAR 的影響

五、最佳實踐建議

  1. 優先使用 getActualMaximum() 而非 getMaximum() 進行邊界檢查。
  2. 跨月/跨年運算請用 add(),單欄位循環用 roll()。
  3. 多執行緒環境下 Calendar 非線程安全,請使用 ThreadLocal 或每次新建實例。
  4. JDK 8+ 新專案建議遷移至 java.time 套件(LocalDate、ZonedDateTime 等),僅維護舊代碼時使用 Calendar。

總結

Calendar 以毫秒時間戳為單一真相來源,透過 complete() → computeFields() 鏈路按需計算 17 個欄位。理解其「延遲計算」、「寬容模式」、「add/roll 語義差異」是正確使用的關鍵。雖然現代 Java 推薦 java.time,但深入掌握 Calendar 內部機制對維護存量系統、排查時區/閏年 Bug 仍具不可替代的價值。

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