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

Java 日期比較與計算工具類:天數差、小時差、區間重疊一站式解決

     編輯:Java 相關知識

前言

業務開發中頻繁遇到「兩個日期誰早誰晚」、「相差幾天/幾小時」、「兩個時間區間是否重疊」等需求。本文整理一套開箱即用的 DateUtils 工具類,基於 SimpleDateFormat + Calendar 實現,適配 Java 7+ 環境;文末另附 Java 8 java.time 現代寫法供升級參考。

核心工具類實現(Java 7 風格)

public final class DateUtils {
    private DateUtils() {}

    private static final String FMT_DT = "yyyy-MM-dd HH:mm:ss";
    private static final String FMT_D  = "yyyy-MM-dd";
    private static final String FMT_H  = "yyyy-MM-dd HH";

    /** 1. 比較兩個日期字符串大小
     * @return 1: dt1>dt2, -1: dt1 overlap(String s1, String e1, String s2, String e2) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(FMT_D);
            Date d1 = sdf.parse(s1), d2 = sdf.parse(e1);
            Date d3 = sdf.parse(s2), d4 = sdf.parse(e3);

            // 區間標準化:確保 start <= end
            if (d1.after(d2)) { Date tmp = d1; d1 = d2; d2 = tmp; }
            if (d3.after(d4)) { Date tmp = d3; d3 = d4; d4 = tmp; }

            // 無重疊判斷
            if (d2.before(d3) || d4.before(d1)) return Collections.emptyMap();

            // 有重疊:取較晚的開始、較早的結束
            Date overlapStart = d1.after(d3) ? d1 : d3;
            Date overlapEnd   = d2.before(d4) ? d2 : d4;

            Map res = new HashMap<>();
            res.put("startDate", sdf.format(overlapStart));
            res.put("endDate",   sdf.format(overlapEnd));
            return res;
        } catch (ParseException ex) {
            throw new IllegalArgumentException("日期格式錯誤", ex);
        }
    }
}

單元測試示例

public class DateUtilsTest {
    @Test
    public void testCompare() {
        assertEquals(1, DateUtils.compare("2023-05-01 12:00:00", "2023-04-30 12:00:00"));
        assertEquals(0, DateUtils.compare("2023-05-01 12:00:00", "2023-05-01 12:00:00"));
    }

    @Test
    public void testDaysBetween() {
        assertEquals(30, DateUtils.daysBetween("2023-04-01", "2023-05-01"));
        assertEquals(-5, DateUtils.daysBetween("2023-05-10", "2023-05-05")); // 負數表示前後順序
    }

    @Test
    public void testOverlap() {
        Map ov = DateUtils.overlap(
            "2023-05-01", "2023-05-10",
            "2023-05-05", "2023-05-15");
        assertEquals("2023-05-05", ov.get("startDate"));
        assertEquals("2023-05-10", ov.get("endDate"));
    }
}

進階:Java 8+ java.time 現代寫法(推薦生產使用)

Java 8 引入的 java.time 包線程安全、API 更直觀,建議新項目直接使用:

public final class ModernDateUtils {
    private static final DateTimeFormatter DT_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final DateTimeFormatter D_FMT  = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    public static int compare(String dt1, String dt2) {
        LocalDateTime l1 = LocalDateTime.parse(dt1, DT_FMT);
        LocalDateTime l2 = LocalDateTime.parse(dt2, DT_FMT);
        return l1.compareTo(l2);
    }

    public static long daysBetween(String d1, String d2) {
        LocalDate ld1 = LocalDate.parse(d1, D_FMT);
        LocalDate ld2 = LocalDate.parse(d2, D_FMT);
        return ChronoUnit.DAYS.between(ld1, ld2);
    }

    public static long hoursBetween(String dt1, String dt2) {
        LocalDateTime l1 = LocalDateTime.parse(dt1, DT_FMT);
        LocalDateTime l2 = LocalDateTime.parse(dt2, DT_FMT);
        return ChronoUnit.HOURS.between(l1, l2);
    }

    public static Map overlap(String s1, String e1, String s2, String e2) {
        LocalDate a1 = LocalDate.parse(s1, D_FMT), a2 = LocalDate.parse(e1, D_FMT);
        LocalDate b1 = LocalDate.parse(s2, D_FMT), b2 = LocalDate.parse(e2, D_FMT);
        if (a1.isAfter(a2)) { LocalDate t = a1; a1 = a2; a2 = t; }
        if (b1.isAfter(b2)) { LocalDate t = b1; b1 = b2; b2 = t; }
        if (a2.isBefore(b1) || b2.isBefore(a1)) return Collections.emptyMap();
        LocalDate start = a1.isAfter(b1) ? a1 : b1;
        LocalDate end   = a2.isBefore(b2) ? a2 : b2;
        return Map.of("startDate", start.format(D_FMT), "endDate", end.format(D_FMT));
    }
}

常見坑點提醒

  • SimpleDateFormat 非線程安全:多線程環境下請使用 ThreadLocal 或每次 new 實例,或直接遷移至 DateTimeFormatter(線程安全)。
  • 時區問題:跨時區系統請顯式指定 TimeZone / ZoneId,避免夏令時導致天數計算偏差。
  • 邊界條件:區間重疊判斷時,需明確「包含邊界」還是「不包含邊界」,業務確認後統一實現。

將上述工具類納入項目公共模塊,即可用一行代碼解決 90% 的日期比較計算需求,減少重複造輪子與 Bug。

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