Java中如何检测所选日期范围与数据库日期范围是否重叠
问题描述
需要实现检测用户选择的日期范围是否与数据库中已存在的日期范围存在重叠冲突。示例场景:
- 用户选择日期范围:24/09/2022 → 30/09/2022
- 数据库已存在日期范围:28/09/2022 → 30/09/2022
此时数据库的预约时段完全被用户选择的时段包含,理应检测到冲突,但当前代码无法返回正确结果。
当前代码逻辑:
boolean isFound = DateHelper.isWithinRange2Dates( /* 用户输入 */ string2Date("24/09/2022"), string2Date("30/09/2022"), /* 数据库数据 */ string2Date("28/09/2022"), string2Date("30/09/2022")); ToastUtils.showLong(isFound ? "FOUND" : "NOT FOUND");
辅助方法:
public static boolean isWithinRange(Date selectedDate, Date startDate, Date endDate) { return selectedDate.after(startDate) && (selectedDate.before(endDate) || DateUtils.isSameDay(selectedDate, endDate)); } public static boolean isWithinRange2Dates(Date selectedStartDate, Date selectedEndDate, Date startDate, Date endDate) { return isWithinRange(selectedStartDate, startDate, endDate) && isWithinRange(selectedEndDate, startDate, endDate); } @SuppressLint("SimpleDateFormat") public static Date string2Date(String dateStr) { try { return new SimpleDateFormat("dd/MM/yyyy").parse(dateStr); } catch (ParseException e) { e.printStackTrace(); } return null; }
问题根源:当前方法仅判断用户的起止日期都完全落在数据库日期范围内时才返回冲突,但实际只要两个范围存在任何重叠(包含、交叉、部分覆盖)都应判定为冲突。
解决方案
核心逻辑
两个日期范围不存在重叠的唯一两种情况:
- 用户选择的结束日期 早于 数据库范围的开始日期
- 用户选择的开始日期 晚于 数据库范围的结束日期
反过来,只要不满足上述两种情况,就说明两个范围存在重叠冲突。
代码实现
推荐使用Java 8+引入的java.time.LocalDate(线程安全、API更清晰,替代旧的Date类):
import java.time.LocalDate; import java.time.format.DateTimeFormatter; public class DateHelper { // 日期格式化器(线程安全) private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("dd/MM/yyyy"); public static LocalDate string2LocalDate(String dateStr) { return LocalDate.parse(dateStr, DATE_FORMATTER); } /** * 检测两个日期范围是否存在重叠 * @param userStart 用户选择的开始日期 * @param userEnd 用户选择的结束日期 * @param dbStart 数据库中的开始日期 * @param dbEnd 数据库中的结束日期 * @return true=存在重叠冲突,false=无冲突 */ public static boolean isDateRangeOverlap(LocalDate userStart, LocalDate userEnd, LocalDate dbStart, LocalDate dbEnd) { // 两种无重叠的情况取反,就是有重叠 return !(userEnd.isBefore(dbStart) || userStart.isAfter(dbEnd)); } }
调用示例:
LocalDate userStart = DateHelper.string2LocalDate("24/09/2022"); LocalDate userEnd = DateHelper.string2LocalDate("30/09/2022"); LocalDate dbStart = DateHelper.string2LocalDate("28/09/2022"); LocalDate dbEnd = DateHelper.string2LocalDate("30/09/2022"); boolean hasConflict = DateHelper.isDateRangeOverlap(userStart, userEnd, dbStart, dbEnd); ToastUtils.showLong(hasConflict ? "时段已有预约" : "可预约");
兼容旧Date类的实现
如果必须使用java.util.Date,可以转换为时间戳比较:
public static boolean isDateRangeOverlap(Date userStart, Date userEnd, Date dbStart, Date dbEnd) { // 转换为时间戳(毫秒)进行比较 long userStartTs = userStart.getTime(); long userEndTs = userEnd.getTime(); long dbStartTs = dbStart.getTime(); long dbEndTs = dbEnd.getTime(); return !(userEndTs < dbStartTs || userStartTs > dbEndTs); }
边界情况说明
- 若业务中认为「用户结束日期等于数据库开始日期」不算重叠(比如用户预约到30号,数据库预约从30号开始),则将
isBefore改为isBeforeOrEqual,isAfter改为isAfterOrEqual(对应LocalDate);Date版本则用<=和>=。 - 上述逻辑覆盖所有重叠场景:包含、交叉、部分覆盖、边界重合。
内容的提问来源于stack exchange,提问作者user7954210
相关产品推荐
相关产品推荐

