業務中頻繁需要計算「結束日期 - 開始日期」的間隔天數,日期字符串可能為 yyyy-MM-dd 或 yyyyMMdd 兩種格式。需封裝通用工具方法,自動識別格式、異常安全、返回 Long 天數。
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 日期工具類
*/
public class DateUtils {
private static final String FORMAT_DASH = "yyyy-MM-dd";
private static final String FORMAT_PLAIN = "yyyyMMdd";
/**
* 計算兩個日期字符串之間的間隔天數 (end - start)
*
* @param startDate 開始日期,支援 yyyy-MM-dd 或 yyyyMMdd
* @param endDate 結束日期,支援 yyyy-MM-dd 或 yyyyMMdd
* @return 間隔天數 (可為負數,表示 start > end)
* @throws ParseException 日期格式無法解析時拋出
*/
public static Long daysBetween(String startDate, String endDate) throws ParseException {
Date start = parseFlexible(startDate);
Date end = parseFlexible(endDate);
// 毫秒差轉天數:24h * 60m * 60s * 1000ms = 86,400,000
long diffMs = end.getTime() - start.getTime();
return diffMs / (24L * 60 * 60 * 1000);
}
/**
* 靈活解析:自動檢測格式並解析
*/
private static Date parseFlexible(String dateStr) throws ParseException {
if (dateStr == null || dateStr.trim().isEmpty()) {
throw new ParseException("日期字符串為空", 0);
}
String trimmed = dateStr.trim();
// 簡單判斷:包含 '-' 按帶分隔符格式解析,否則按純數字格式
if (trimmed.contains("-")) {
return new SimpleDateFormat(FORMAT_DASH).parse(trimmed);
} else {
return new SimpleDateFormat(FORMAT_PLAIN).parse(trimmed);
}
}
}
public class DateUtilsTest {
public static void main(String[] args) {
try {
System.out.println(DateUtils.daysBetween("2024-01-01", "2024-01-10")); // 9
System.out.println(DateUtils.daysBetween("20240101", "20240110")); // 9
System.out.println(DateUtils.daysBetween("2024-12-31", "2024-01-01")); // -365 (負數)
System.out.println(DateUtils.daysBetween("20240228", "20240301")); // 2 (閏年)
} catch (ParseException e) {
System.err.println("日期格式錯誤: " + e.getMessage());
}
}
}
SimpleDateFormat 非線程安全,生產環境建議用 ThreadLocal 或 Java 8+ DateTimeFormatter (線程安全)。import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
public static long daysBetweenModern(String start, String end) {
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy[-MM][-dd][yyyyMMdd]"); // 非標準,建議分兩次試
// 實際建議:嘗試解析,失敗則換格式
LocalDate startDate = parseLocalDateFlexible(start);
LocalDate endDate = parseLocalDateFlexible(end);
return ChronoUnit.DAYS.between(startDate, endDate);
}
Date 依賴 JVM 默認時區,跨時區場景必須顯式指定時區或改用 LocalDate (無時區)。+1,需明確需求文檔。提供了一個健壯的 daysBetween 工具方法,支援雙格式自適應、異常拋出、負數處理。現代項目強烈建議遷移至 java.time 包 (LocalDate/ChronoUnit),享受線程安全、不可變、API 清晰的紅利。