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

Java 日期時間工具類完整版:LocalDate 時代的最佳實踐與兼容舊 API

     編輯:Java 相關知識

現代首選:Java 8+ java.time 包

線程安全、不可變、API 豐富,完全替代 Date/Calendar/SimpleDateFormat。

核心類

  • LocalDate:日期 (年月日)
  • LocalTime:時間 (時分秒納秒)
  • LocalDateTime:日期時間
  • Instant:時間戳 (UTC)
  • ZonedDateTime:帶時區
  • DateTimeFormatter:格式化/解析 (線程安全)
  • Duration / Period:時間間隔

常用操作

// 當前日期
LocalDate today = LocalDate.now();

// 加減天數
LocalDate tomorrow = today.plusDays(1);
LocalDate yesterday = today.minusDays(1);

// 格式化
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String str = today.format(fmt);           // "2024-01-15"
LocalDate parsed = LocalDate.parse(str, fmt);

// 兩日期間天數
long days = ChronoUnit.DAYS.between(start, end);

// 日期區間流式生成
List<LocalDate> list = start.datesUntil(end.plusDays(1)).collect(toList());

// 舊 API 互轉
Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
LocalDateTime ldt = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());

兼容舊項目:Date/Calendar 工具類 (線程安全版)

public class DateUtils {
    private static final ThreadLocal<SimpleDateFormat> TL_SDF = 
        ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));

    // 指定格式
    public static SimpleDateFormat getFormatter(String pattern) {
        return new SimpleDateFormat(pattern); // 每次新建,或用 Map 池化
    }

    // Date → String
    public static String format(Date date, String pattern) {
        return getFormatter(pattern).format(date);
    }

    // String → Date
    public static Date parse(String str, String pattern) throws ParseException {
        return getFormatter(pattern).parse(str);
    }

    // 獲取前一天 / 後一天 (Date 版)
    public static Date addDays(Date date, int days) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(Calendar.DAY_OF_MONTH, days);
        return cal.getTime();
    }

    // 日期區間列表 (Date 版)
    public static List<Date> range(Date start, Date end, String pattern) {
        List<Date> list = new ArrayList<>();
        Date cur = start;
        while (!cur.after(end)) {
            list.add(cur);
            cur = addDays(cur, 1);
        }
        return list;
    }

    // 兩 Date 比較 (忽略時分秒)
    public static boolean sameDay(Date d1, Date d2) {
        return format(d1, "yyyyMMdd").equals(format(d2, "yyyyMMdd"));
    }

    // 判斷是否在範圍內
    public static boolean between(Date target, Date start, Date end) {
        return !target.before(start) && !target.after(end);
    }
}

工具類設計原則

  1. 無狀態、全靜態方法,易於調用
  2. 線程安全:SimpleDateFormat 用 ThreadLocal 或每次新建;DateTimeFormatter 本身線程安全可共享
  3. 參數校驗:空值檢查、格式異常拋出明確異常
  4. 時區顯式:跨時區系統必須指定 ZoneId
  5. 不可變性:所有操作返回新對象,不修改入參

常見坑與避坑

坑原因解決
SimpleDateFormat 線程不安全內部共享 CalendarThreadLocal / 每次新建 / DateTimeFormatter
yyyy vs YYYYYYYY 是週年,跨年周會導致年份錯誤嚴格用 yyyy
MM vs mmMM 月份,mm 分鐘仔細核對模式字符串
Date 忽略時區Date 本質是 UTC 毫秒數,toString 用默認時區顯式指定時區格式化
LocalDate.parse 拋異常格式不匹配預校驗或捕獲 DateTimeParseException
夏令時導致 23/25 小時Calendar.add(DAY) 可能跨越 DST 切換點用 LocalDate 或 Instant 計算

測試用例

@Test
public void testRange() {
    LocalDate start = LocalDate.of(2024, 1, 1);
    LocalDate end = LocalDate.of(2024, 1, 5);
    List<LocalDate> list = start.datesUntil(end.plusDays(1)).collect(toList());
    assertEquals(5, list.size());
    assertEquals(LocalDate.of(2024, 1, 3), list.get(2));
}

@Test
public void testLegacyCompat() {
    Date now = new Date();
    Date tomorrow = DateUtils.addDays(now, 1);
    assertTrue(tomorrow.after(now));
    String str = DateUtils.format(now, "yyyy-MM-dd");
    Date parsed = DateUtils.parse(str, "yyyy-MM-dd");
    assertTrue(DateUtils.sameDay(now, parsed));
}

總結

新代碼全面遷移 java.time,舊項目封裝 DateUtils 並標註 @Deprecated 引導遷移。核心口訣:

  1. 線程安全:DateTimeFormatter 共享,SimpleDateFormat 隔離
  2. 不可變:所有操作返回新對象
  3. 時區顯式:ZoneId.systemDefault() 或業務時區
  4. 區間計算:ChronoUnit.between / datesUntil
  5. 格式化:yyyy-MM-dd 而非 YYYY-MM-dd

掌握這套體系,日期時間處理不再是 Bug 溫床。

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