C#中如何编写预约时间与排班时段的冲突判断条件?
解决C#预约时间与HR午餐时段冲突的判断问题
原代码的核心问题
- 重复解析DateTime:多次调用
DateTime.Parse既降低性能,又增加出错风险,且代码可读性极差。 - 逻辑覆盖不全:原判断仅检查预约开始时间是否落在午餐时段内,未覆盖预约时间段与午餐时段部分重叠的场景(比如你的示例中11:30-14:30与13:00-14:00重叠,但原代码无法检测到)。
- 变量未定义:代码中使用了
currentDate变量,但你提供的变量示例中无此值,会直接导致运行错误。 - 时间解析隐患:
currentStartTime是"1pm",直接用InvariantCulture.Parse可能因区域性设置问题解析失败,更可靠的方式是解析为TimeSpan。
正确的判断逻辑实现
步骤1:解析所有时间变量(仅一次)
先将所有字符串格式的时间/日期解析为DateTime或TimeSpan对象,避免重复操作:
using System; using System.Globalization; // 解析预约的完整时间段 if (!DateTime.TryParse(currentAptStartTime, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime aptStart)) { throw new ArgumentException("预约开始时间格式无效"); } if (!DateTime.TryParse(currentAptEndTime, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime aptEnd)) { throw new ArgumentException("预约结束时间格式无效"); } // 解析HR午餐的日期范围 if (!DateTime.TryParse(currentStartDate, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime lunchDateStart)) { throw new ArgumentException("午餐起始日期格式无效"); } if (!DateTime.TryParse(currentEndDate, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime lunchDateEnd)) { throw new ArgumentException("午餐结束日期格式无效"); } // 解析每日午餐的时间区间(转换为TimeSpan) if (!TimeSpan.TryParse(currentStartTime, CultureInfo.InvariantCulture, out TimeSpan lunchDailyStart)) { throw new ArgumentException("午餐开始时间格式无效"); } if (!TimeSpan.TryParse(currentEndTime, CultureInfo.InvariantCulture, out TimeSpan lunchDailyEnd)) { throw new ArgumentException("午餐结束时间格式无效"); }
步骤2:判断时间段是否重叠
核心逻辑:只要预约时间段与任何一天的午餐时间段存在重叠,就禁止创建预约。
先封装一个判断两个时间段是否重叠的工具方法(采用左闭右开规则,即[start1, end1)和[start2, end2)):
private static bool IsTimeOverlap(DateTime start1, DateTime end1, DateTime start2, DateTime end2) { // 左闭右开的重叠判断:start1 < end2 且 start2 < end1 return start1 < end2 && start2 < end1; }
然后遍历所有需要检查的日期(即预约日期与午餐日期范围的交集):
// 确定需要检查的日期范围:预约和午餐日期的重叠区间 DateTime checkStartDate = DateTime.MaxValue(aptStart.Date, lunchDateStart.Date); DateTime checkEndDate = DateTime.MinValue(aptEnd.Date, lunchDateEnd.Date); // 如果没有日期重叠,直接允许预约 if (checkStartDate > checkEndDate) { return true; } // 遍历每一天,检查当天的午餐时段是否与预约重叠 for (DateTime date = checkStartDate; date <= checkEndDate; date = date.AddDays(1)) { DateTime dailyLunchStart = date.Date.Add(lunchDailyStart); DateTime dailyLunchEnd = date.Date.Add(lunchDailyEnd); // 检查当前午餐时段是否与预约时间段重叠 if (IsTimeOverlap(aptStart, aptEnd, dailyLunchStart, dailyLunchEnd)) { // 存在重叠,禁止创建预约 return false; } } // 无重叠,允许创建预约 return true;
完整示例代码
将上述逻辑整合为一个可调用的方法:
using System; using System.Globalization; public class AppointmentValidator { public bool CanCreateAppointment(string currentAptStartTime, string currentAptEndTime, string currentStartDate, string currentEndDate, string currentStartTime, string currentEndTime) { // 解析预约时间 if (!DateTime.TryParse(currentAptStartTime, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime aptStart) || !DateTime.TryParse(currentAptEndTime, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime aptEnd)) { throw new ArgumentException("预约时间格式无效"); } // 解析午餐日期范围 if (!DateTime.TryParse(currentStartDate, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime lunchDateStart) || !DateTime.TryParse(currentEndDate, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime lunchDateEnd)) { throw new ArgumentException("午餐日期范围格式无效"); } // 解析每日午餐时间 if (!TimeSpan.TryParse(currentStartTime, CultureInfo.InvariantCulture, out TimeSpan lunchDailyStart) || !TimeSpan.TryParse(currentEndTime, CultureInfo.InvariantCulture, out TimeSpan lunchDailyEnd)) { throw new ArgumentException("午餐时段格式无效"); } // 确定需要检查的日期区间 DateTime checkStart = DateTime.MaxValue(aptStart.Date, lunchDateStart.Date); DateTime checkEnd = DateTime.MinValue(aptEnd.Date, lunchDateEnd.Date); if (checkStart > checkEnd) { return true; } // 遍历每一天检查重叠 for (DateTime date = checkStart; date <= checkEnd; date = date.AddDays(1)) { DateTime dailyLunchStart = date.Date.Add(lunchDailyStart); DateTime dailyLunchEnd = date.Date.Add(lunchDailyEnd); if (IsTimeOverlap(aptStart, aptEnd, dailyLunchStart, dailyLunchEnd)) { return false; } } return true; } private static bool IsTimeOverlap(DateTime start1, DateTime end1, DateTime start2, DateTime end2) { // 左闭右开规则:两个时间段有重叠的条件 return start1 < end2 && start2 < end1; } }
最佳实践
- 优先使用TryParse系列方法:避免
Parse在格式错误时抛出未处理的异常,提升代码健壮性。 - 避免重复解析DateTime:解析一次后存储为变量,既提升性能,也让代码更易读。
- 明确时间段边界规则:提前约定是左闭右开还是闭区间,避免因边界判断歧义导致逻辑错误(比如14:00是否属于午餐时段)。
- 封装重复逻辑:将时间段重叠判断封装为独立方法,便于复用和维护。
- 使用清晰的变量命名:比如将
currentAptStartTime改为appointmentFullStart,明确变量包含日期和时间,避免混淆。 - 考虑时区问题:如果系统涉及跨时区,使用
DateTimeOffset代替DateTime,确保时间判断的准确性。 - 添加错误处理:对解析失败的情况返回明确的错误信息,方便调试和用户理解。
内容的提问来源于stack exchange,提问作者Fatt Sky
相关产品推荐
相关产品推荐

