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

java.util.Calendar 完整使用指南與日期時間操作實務

     編輯:Java 相關知識

前言

java.util.Calendar 是 JDK 1.1 引入的抽象類別,提供**曆法欄位操作**、**日期運算**、**時區處理**等功能。雖然 JDK 8 引入更現代的 java.time 套件 (LocalDateTime, ZonedDateTime 等),但 Calendar 在大量舊系統、Android 專案、特定曆法需求 (佛曆、日曆) 中仍廣泛使用。本文完整說明 Calendar 常用欄位、操作方法、時區處理與遷移建議。

核心概念

  • 抽象基類:Calendar 為抽象類,實際使用具體實作 GregorianCalendar (格里曆/公曆)。
  • 欄位常數:YEAR, MONTH (0-11), DATE/DAY_OF_MONTH, HOUR (0-11), HOUR_OF_DAY (0-23), MINUTE, SECOND, MILLISECOND, DAY_OF_WEEK, WEEK_OF_YEAR 等。
  • 可變性:Calendar 物件**可變**,多執行緒環境**非安全**,需自行同步或使用 ThreadLocal。

基本用法範例

import java.util.*;

public class CalendarDemo {
    public static void main(String[] args) {
        // 1. 取得預設時區/地區的 GregorianCalendar 實例
        Calendar cal = Calendar.getInstance(); // 等同 new GregorianCalendar()
        
        // 2. 設定特定時間 (可選)
        Date now = new Date();
        cal.setTime(now);
        
        // 3. 讀取各欄位
        printFields(cal, "目前時間");
        
        // 4. 日期運算:加減天數、月份、年份
        cal.add(Calendar.DATE, 10);      // 10 天後
        printFields(cal, "10 天後");
        
        cal.add(Calendar.MONTH, -3);     // 3 個月前
        printFields(cal, "3 個月前");
        
        // 5. 設定特定欄位 (清除較低精度欄位避免殘留)
        cal.clear(Calendar.HOUR_OF_DAY);
        cal.set(Calendar.HOUR_OF_DAY, 15); // 設為下午 3 點
        cal.clear(Calendar.MINUTE);
        cal.set(Calendar.MINUTE, 30);
        cal.clear(Calendar.SECOND);
        cal.clear(Calendar.MILLISECOND);
        printFields(cal, "設定為 15:30:00");
        
        // 6. 時區資訊
        System.out.println("時區偏移 (小時): " + cal.get(Calendar.ZONE_OFFSET) / (60*60*1000));
        System.out.println("夏令時間偏移 (小時): " + cal.get(Calendar.DST_OFFSET) / (60*60*1000));
    }
    
    static void printFields(Calendar cal, String label) {
        System.out.println("\n=== " + label + " ===");
        System.out.println("ERA: " + cal.get(Calendar.ERA)); // 1=AD, 0=BC
        System.out.println("YEAR: " + cal.get(Calendar.YEAR));
        System.out.println("MONTH: " + (cal.get(Calendar.MONTH) + 1)); // 0-based → 1-12
        System.out.println("DATE: " + cal.get(Calendar.DATE));
        System.out.println("DAY_OF_WEEK: " + cal.get(Calendar.DAY_OF_WEEK)); // 1=Sun...7=Sat
        System.out.println("HOUR_OF_DAY: " + cal.get(Calendar.HOUR_OF_DAY));
        System.out.println("MINUTE: " + cal.get(Calendar.MINUTE));
        System.out.println("SECOND: " + cal.get(Calendar.SECOND));
        System.out.println("MILLISECOND: " + cal.get(Calendar.MILLISECOND));
        System.out.println("ZONE_OFFSET: " + cal.get(Calendar.ZONE_OFFSET)/(60*60*1000) + "h");
        System.out.println("DST_OFFSET: " + cal.get(Calendar.DST_OFFSET)/(60*60*1000) + "h");
    }
}

關鍵操作方法對照表

常見陷阱與最佳實務

1. MONTH 為 0-based (0=一月, 11=十二月)

cal.set(Calendar.MONTH, 0); // 一月,非 1
System.out.println(cal.get(Calendar.MONTH) + 1); // 顯示時 +1

2. set() 不立即正規化,需 getTime() 觸發

cal.set(Calendar.MONTH, 1); // 二月
cal.set(Calendar.DATE, 31); // 設為 2/31 (不存在)
Date d = cal.getTime();     // 自動正規化 → 3/2 或 3/3 (視閏年)
System.out.println(d);

3. 多執行緒不安全

// 錯誤:共享單一 Calendar 實例
static Calendar shared = Calendar.getInstance(); 

// 正確:ThreadLocal 隔離
static ThreadLocal threadCal = ThreadLocal.withInitial(Calendar::getInstance);

4. 時區處理

Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Asia/Taipei"));
// 或
cal.setTimeZone(TimeZone.getTimeZone("UTC"));

與 java.time (JDK 8+) 互通遷移

// Calendar → Instant → ZonedDateTime
Calendar cal = Calendar.getInstance();
Instant instant = cal.toInstant();
ZonedDateTime zdt = instant.atZone(ZoneId.of("Asia/Taipei"));

// ZonedDateTime → Calendar
ZonedDateTime now = ZonedDateTime.now();
Calendar cal2 = Calendar.from(now.toInstant());
cal2.setTimeZone(TimeZone.getTimeZone(now.getZone()));

遷移建議對照

操作方法說明
取得單一欄位get(int field)回傳 int 值
設定單一欄位set(int field, int value)不正規化,需呼叫 getTime() 才重新計算
日期加減 (推薦)add(int field, int amount)正規化處理月份天數差異、閏年、夏令時間
日期滾動 (不進位)roll(int field, int amount)如 1/31 roll 月份 +1 → 2/28 (不改變年份)
清除欄位clear(int field) / clear()設為未定義狀態,下次 get/計算時重新推導
轉換 DategetTime() / setTime(Date)與舊 API 互通
比較時間before(Object), after(Object), compareTo(Calendar)比較時間點
複製clone()建立獨立副本

特殊曆法支援

JDK 內建 JapaneseImperialCalendar (和曆)、BuddhistCalendar (佛曆)。第三方庫 (ICU4J) 提供農曆、波斯曆、伊斯蘭曆等完整支援。

總結

  • Calendar 適用於**舊系統維護、Android (API < 26)、特殊曆法需求**。
  • 核心操作:getInstance()、get/set/add/roll/clear、getTime()/setTime()。
  • 注意:**MONTH 0-based、set 非即時正規化、非執行緒安全、時區明確設定**。
  • **新專案優先使用 java.time**,舊系統逐步遷移,邊界轉換用 toInstant()/Calendar.from()。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
Calendarjava.time 替代優勢
Calendar.getInstance()LocalDateTime.now() / ZonedDateTime.now()不可變、執行緒安全、API 語意清晰
cal.add(DAY, 1)date.plusDays(1)流式 API、不可變
cal.get(YEAR)date.getYear()型別安全、方法名直觀
cal.setTimeZone(tz)zdt.withZoneSameInstant(zone)明確區分 Instant/Local/Zoned
SimpleDateFormatDateTimeFormatter執行緒安全、預定義格式