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

Java 計算兩個日期之間天數差的正確姿勢

     編輯:Java 相關知識

計算日期天數差看似簡單,實則暗藏時區、夏令時、毫秒精度等陷阱。本文分析常見錯誤,提供基於 Calendar 歸零時間與 Java 8 LocalDate 兩種標準實現,確保跨時區、跨夏令時正確計算。

常見錯誤示範


// 錯誤 1:直接用毫秒差除以一天毫秒數
long diff = date2.getTime() - date1.getTime();
int days = (int) (diff / (1000 * 60 * 60 * 24)); 
// 問題:夏令時切換日非 24 小時,時區偏移導致誤差

// 錯誤 2:SimpleDateFormat 解析後直接相減
// 同樣受時區、夏令時影響

方案一:Calendar 歸零時間(Java 7 及之前)


import java.util.Calendar;
import java.util.Date;

public class DateDiffUtils {

    /**
     * 計算兩個日期之間的天數差(date1 - date2)
     * 忽略時間部分,僅比較年月日
     */
    public static int daysBetween(Date date1, Date date2) {
        Calendar cal1 = Calendar.getInstance();
        Calendar cal2 = Calendar.getInstance();
        cal1.setTime(date1);
        cal2.setTime(date2);
        
        // 關鍵:將時間歸零到當天 00:00:00.000
        setTimeToMidnight(cal1);
        setTimeToMidnight(cal2);
        
        long ms1 = cal1.getTimeInMillis();
        long ms2 = cal2.getTimeInMillis();
        long diffMs = ms1 - ms2;
        
        return millisecondsToDays(diffMs);
    }

    private static int millisecondsToDays(long ms) {
        return (int) (ms / (1000L * 60 * 60 * 24));
    }

    private static void setTimeToMidnight(Calendar cal) {
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
    }
}

方案二:Java 8 LocalDate(推薦,現代標準)


import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;

public class DateDiffUtils {

    /**
     * 計算天數差(Java 8+ 推薦)
     * @param date1 日期 1
     * @param date2 日期 2
     * @param zoneId 時區(如 ZoneId.systemDefault())
     * @return date1 到 date2 的天數差
     */
    public static long daysBetween(Date date1, Date date2, ZoneId zoneId) {
        LocalDate ld1 = date1.toInstant().atZone(zoneId).toLocalDate();
        LocalDate ld2 = date2.toInstant().atZone(zoneId).toLocalDate();
        return java.time.temporal.ChronoUnit.DAYS.between(ld2, ld1);
    }

    // 重載:使用系統預設時區
    public static long daysBetween(Date date1, Date date2) {
        return daysBetween(date1, date2, ZoneId.systemDefault());
    }

    // 直接接受 LocalDate
    public static long daysBetween(LocalDate date1, LocalDate date2) {
        return ChronoUnit.DAYS.between(date2, date1);
    }
}

使用範例


import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;

public class Demo {
    public static void main(String[] args) {
        // 構造測試日期
        LocalDate d1 = LocalDate.of(2024, 1, 15);
        LocalDate d2 = LocalDate.of(2024, 1, 1);
        
        System.out.println("LocalDate 直接計算: " + 
            DateDiffUtils.daysBetween(d1, d2)); // 14
        
        // Date 對象轉換
        Date date1 = Date.from(d1.atStartOfDay(ZoneId.systemDefault()).toInstant());
        Date date2 = Date.from(d2.atStartOfDay(ZoneId.systemDefault()).toInstant());
        
        System.out.println("Date 計算 (Calendar): " + 
            DateDiffUtils.daysBetween(date1, date2)); // 14
        System.out.println("Date 計算 (LocalDate): " + 
            DateDiffUtils.daysBetween(date1, date2)); // 14
    }
}

關鍵差異對比

特性Calendar 歸零法LocalDate (Java 8+)
API 設計舊式、可變、易錯新式、不可變、線程安全
時區處理隱式使用系統預設顯式指定 ZoneId
夏令時正確性歸零後通常正確內建正確處理
可讀性較差極佳
效能一般更好

特殊情況處理

包含起始日還是結束日?


// 差值定義:date1 - date2
// 2024-01-01 到 2024-01-02 = 1 天
// 2024-01-01 到 2024-01-01 = 0 天

// 若需「包含兩端」天數(如住宿天數):
long inclusiveDays = daysBetween(date1, date2) + 1;

跨時區計算


// 紐約時間 2024-03-10 23:00 (夏令時前) 
// 倫敦時間 2024-03-11 04:00 (同一時刻)
// 若用各自本地日期計算天數差可能不同
// 解決:統一指定時區或使用 UTC
long days = daysBetween(date1, date2, ZoneId.of("UTC"));

Joda-Time(舊專案遷移過渡)


// Joda-Time 寫法
import org.joda.time.Days;
import org.joda.time.LocalDate;

int days = Days.daysBetween(new LocalDate(date1), new LocalDate(date2)).getDays();

總結建議

  1. 新專案:直接使用 java.time.LocalDate + ChronoUnit.DAYS.between()
  2. 舊專案 (Java 7):使用 Calendar 歸零時間法,注意設定 MILLISECOND
  3. 避免:直接用毫秒數除以 86400000,會在夏令時切換日產生誤差
  4. 明確:業務上是「間隔天數」還是「包含端點天數」,差 1 很常見

正確處理日期天數差,是避免計費、統計、排程等核心業務出錯的關鍵基礎。

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