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

Java 日期時間處理大全:Calendar、Date、格式化、間隔計算最佳實踐

     編輯:Java 相關知識

核心類別關係

  • `java.util.Date`:毫秒時間戳載體,多數方法已棄用
  • `java.util.Calendar`:抽象曆法系統,`GregorianCalendar` 為預設實作,可操作年月日時分秒欄位
  • `java.text.SimpleDateFormat`:非執行緒安全 的格式化/解析工具
  • Java 8+ 新 API:`java.time` 套件 (`LocalDateTime`, `ZonedDateTime`, `DateTimeFormatter`) 為不可變、執行緒安全、設計更佳的替代方案

常用操作速查 (Legacy API)

1. 取得某月最大天數

Calendar cal = Calendar.getInstance();
cal.clear(); // 必須先清除,否則繼承當前時間殘留值
cal.set(Calendar.YEAR, 2024);
cal.set(Calendar.MONTH, 1); // 2月 (0-based)
int maxDay = cal.getActualMaximum(Calendar.DAY_OF_MONTH); // 29 (閏年)

2. Calendar ⇄ Date 雙向轉換

// Calendar → Date
Date date = cal.getTime();

// Date → Calendar
Calendar cal2 = Calendar.getInstance();
cal2.setTime(date);

3. 格式化輸出

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 24小時制 HH
String formatted = sdf.format(new Date()); // "2024-01-15 14:30:45"

// 解析
Date parsed = sdf.parse("2024-01-15 14:30:45");

⚠️ `SimpleDateFormat` 非執行緒安全,多執行緒環境必須:

  • 方法區域變數建立新實例
  • 或使用 `ThreadLocal` 快取
  • 或遷移至 Java 8 `DateTimeFormatter` (不可變、安全)

4. 週期計算:第幾週 / 某週第一天

// 某天是第幾週
Calendar cal = Calendar.getInstance();
cal.set(2024, Calendar.JANUARY, 1);
int weekOfYear = cal.get(Calendar.WEEK_OF_YEAR); // 1

// 第 1 週的週一是幾號
cal.set(2024, Calendar.JANUARY, 1);
cal.set(Calendar.WEEK_OF_YEAR, 1);
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
System.out.println(sdf.format(cal.getTime())); // 2024-01-01

5. add() vs roll() 關鍵差異

Calendar cal = Calendar.getInstance();
cal.set(2024, Calendar.SEPTEMBER, 3); // 9月3日

cal.add(Calendar.DATE, -4);  // 向前4天 → 8月30日 (月份自動進位)
cal.add(Calendar.DATE, 4);   // 回到 9月3日

cal.set(2024, Calendar.SEPTEMBER, 3);
cal.roll(Calendar.DATE, -4); // 本月內循環 → 8月30日? 不! roll不進位月份 → 9月29日
cal.roll(Calendar.DATE, 4);  // → 9月3日

結論:幾乎永遠用 `add()`,`roll()` 僅適用「同欄位循環」如時鐘表盤。

6. 兩日期間隔天數計算

// 方案 A:毫秒差值 (純時間跨度)
public static long daysBetween(Date start, Date end) {
    if (start.after(end)) { Date t = start; start = end; end = t; }
    return (end.getTime() - start.getTime()) / (1000L * 60 * 60 * 24);
}

// 方案 B:Calendar 自然日差 (忽略時分秒)
public static int calendarDaysBetween(Calendar d1, Calendar d2) {
    if (d1.after(d2)) { Calendar t = d1; d1 = d2; d2 = t; }
    int days = d2.get(Calendar.DAY_OF_YEAR) - d1.get(Calendar.DAY_OF_YEAR);
    int y2 = d2.get(Calendar.YEAR);
    if (d1.get(Calendar.YEAR) != y2) {
        d1 = (Calendar) d1.clone();
        do {
            days += d1.getActualMaximum(Calendar.DAY_OF_YEAR); // 當年總天數
            d1.add(Calendar.YEAR, 1);
        } while (d1.get(Calendar.YEAR) != y2);
    }
    return days;
}

💡 方案 B 解決「跨年」與「23:00 到隔日 01:00 算 1 天」等業務語義差異。

Java 8+ 現代 API 遷移指南

LegacyModern (java.time)優勢
`Date``Instant` / `LocalDateTime`不可變、納秒精度
`Calendar``ZonedDateTime` / `LocalDate`領域模型清晰、時區支援完整
`SimpleDateFormat``DateTimeFormatter`執行緒安全、預定義格式常數
手算間隔`ChronoUnit.DAYS.between(d1, d2)`語義明確、支援各時間單位
`TimeZone``ZoneId`IANA 時區資料庫、歷史變更支援
// Java 8 範例
LocalDate today = LocalDate.now();
LocalDate firstDayOfMonth = today.withDayOfMonth(1);
int daysInMonth = today.lengthOfMonth(); // 直接取得
long daysBetween = ChronoUnit.DAYS.between(startDate, endDate);

// 格式化
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String str = zdt.format(fmt);
ZonedDateTime parsed = ZonedDateTime.parse(str, fmt);

避坑清單

  1. `Calendar.MONTH` 0-based (0=1月),`Calendar.DAY_OF_MONTH` 1-based
  2. `SimpleDateFormat` 模式 `yyyy` vs `YYYY` (週年)、`HH` vs `hh` (24/12小時)
  3. 資料庫 `TIMESTAMP` 帶時區,`DATETIME` 不帶 — 讀寫需明確時區
  4. 序列化傳輸一律用 ISO-8601 (`2024-01-15T14:30:45Z`)
  5. 專案強制遷移 `java.time`,Legacy API 僅維護舊代碼
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved