業務常見需求:給定起始與結束日期,生成區間內所有日期列表。本文示範使用 Calendar API 實現日期區間拆分,並說明現代 java.time 替代方案。
import java.util.*;
import java.util.Calendar;
public class DateRangeSplitter {
public static List<Date> splitDateRange(Date startDate, Date endDate) {
List<Date> result = new ArrayList<>();
Calendar start = Calendar.getInstance();
start.setTime(startDate);
// 歸零時分秒毫秒,僅比較日期
start.set(Calendar.HOUR_OF_DAY, 0);
start.set(Calendar.MINUTE, 0);
start.set(Calendar.SECOND, 0);
start.set(Calendar.MILLISECOND, 0);
Calendar end = Calendar.getInstance();
end.setTime(endDate);
end.set(Calendar.HOUR_OF_DAY, 0);
end.set(Calendar.MINUTE, 0);
end.set(Calendar.SECOND, 0);
end.set(Calendar.MILLISECOND, 0);
// 逐日迭代
while (!start.after(end)) {
result.add(start.getTime());
start.add(Calendar.DATE, 1); // 增加一天
}
return result;
}
public static void main(String[] args) {
// 範例:2024-06-01 至 2024-06-05
Calendar cal = Calendar.getInstance();
cal.set(2024, Calendar.JUNE, 1);
Date start = cal.getTime();
cal.set(2024, Calendar.JUNE, 5);
Date end = cal.getTime();
List<Date> dates = splitDateRange(start, end);
SimpleDateFormat sdf = new SimpleDateFormat("MM-dd");
for (Date d : dates) {
System.out.println(sdf.format(d));
}
// 輸出:
// 06-01
// 06-02
// 06-03
// 06-04
// 06-05
}
}
Calendar.DATE / Calendar.DAY_OF_MONTH 等價add() 自動處理月份進位、閏年等邊界情況!start.after(end) 而非 before,包含結束日期import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.stream.*;
public class ModernDateRangeSplitter {
public static List<LocalDate> splitDateRange(LocalDate start, LocalDate end) {
return start.datesUntil(end.plusDays(1))
.collect(Collectors.toList());
}
public static void main(String[] args) {
List<LocalDate> dates = splitDateRange(
LocalDate.of(2024, 6, 1),
LocalDate.of(2024, 6, 5)
);
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("MM-dd");
dates.forEach(d -> System.out.println(d.format(fmt)));
}
}
優勢:不可變、線程安全、流式 API、時區處理清晰、無需手動歸零。
| 場景 | 建議 |
|---|---|
| 短區間(< 1 年) | 直接收集為 List |
| 長區間 / 串流處理 | 返回 Stream<LocalDate> 惰性處理 |
| 極大區間(年級) | 分批處理,避免 OOM |
Date 可變性導致非預期修改Date 產生偏差Calendar.MONTH 從 0 開始(1月=0)public final class DateUtils {
private DateUtils() {}
public static List<LocalDate> getDateRange(LocalDate start, LocalDate end) {
if (start.isAfter(end)) throw new IllegalArgumentException("起始日期不可晚於結束日期");
return start.datesUntil(end.plusDays(1)).collect(toList());
}
public static Stream<LocalDate> dateRangeStream(LocalDate start, LocalDate end) {
return start.datesUntil(end.plusDays(1));
}
}
日期區間拆分看似簡單,邊界條件與時區處理卻暗藏坑點。新專案強烈建議使用 java.time,舊專案維護時以 Calendar 歸零時間分量為關鍵防禦點。