如何在存在重叠日期的两列DateTime中查找时间间隙?
问题分析与解决方案
你的代码里有两个关键问题导致TimePeriod库无法识别重叠时段内的小时间隙:
- 排序未生效:
organizeCurvePoints.OrderBy(t => t.EffectiveTime);这行仅生成了排序后的序列,但未赋值回原集合,原列表仍保持无序状态,会导致后续合并时段时逻辑混乱。 - 自定义类未实现ITimePeriod接口:TimePeriod库的
CombinePeriods方法需要处理实现ITimePeriod接口的对象,你的CurveDateOrganizer只是普通类,库无法正确识别它的时间范围,合并后的时段自然不准确,间隙计算也就出错了。
修复后的代码实现
方案1:让CurveDateOrganizer实现ITimePeriod接口
修改自定义类,实现ITimePeriod接口,让库能正确解析时间范围:
[Serializable] [DebuggerDisplay("EffectiveTime {EffectiveTime} TerminationTime {TerminationTime}", Name = "{DateOrganizer}")] public class CurveDateOrganizer : ITimePeriod { private DateTime _effectiveTime; private DateTime _terminationTime; public DateTime EffectiveTime { get => _effectiveTime; set { if (_effectiveTime != value) { _effectiveTime = value; } } } public DateTime TerminationTime { get => _terminationTime; set { if (_terminationTime != value) { _terminationTime = value; } } } // 实现ITimePeriod接口的属性和方法 public DateTime Start => EffectiveTime; public DateTime End => TerminationTime; public TimeSpan Duration => TerminationTime - EffectiveTime; public bool IsReadOnly => false; public bool IsMoment => EffectiveTime == TerminationTime; public void Move(TimeSpan offset) { EffectiveTime += offset; TerminationTime += offset; } public void SetStart(DateTime start) { EffectiveTime = start; } public void SetEnd(DateTime end) { TerminationTime = end; } public void SetDuration(TimeSpan duration) { TerminationTime = EffectiveTime + duration; } public bool HasInside(DateTime moment) { return moment >= EffectiveTime && moment < TerminationTime; } public bool HasInside(ITimePeriod period) { return period.Start >= EffectiveTime && period.End <= TerminationTime; } public bool IntersectsWith(ITimePeriod period) { return !(period.End <= EffectiveTime || period.Start >= TerminationTime); } public bool OverlapsWith(ITimePeriod period) { return period.Start < TerminationTime && period.End > EffectiveTime; } public TimeSpan GetIntersectionDuration(ITimePeriod period) { var intersectStart = Max(EffectiveTime, period.Start); var intersectEnd = Min(TerminationTime, period.End); return intersectEnd > intersectStart ? intersectEnd - intersectStart : TimeSpan.Zero; } private DateTime Max(DateTime a, DateTime b) => a > b ? a : b; private DateTime Min(DateTime a, DateTime b) => a < b ? a : b; public CurveDateOrganizer(DateTime localEffectiveTime, DateTime localTerminationTime) { _effectiveTime = localEffectiveTime; _terminationTime = localTerminationTime; } }
方案2:直接使用TimeRange替代自定义类(更简洁)
如果不需要自定义类的额外逻辑,直接用TimePeriod库自带的TimeRange会更省事:
// 替换原自定义类逻辑,直接生成TimeRange集合 var curvePeriodDates = new TimePeriodCollection(); foreach (var d in AllCurvePointsInRangeCopy) { curvePeriodDates.Add(new TimeRange(d.LocalEffectiveTime, d.LocalTerminationTime)); } // 按起始时间排序,确保时段顺序正确 curvePeriodDates.SortByStart(); // 定义总时间范围 var totalPeriod = new TimeRange(startDate, endDate); // 合并所有有效时段 var combinedPeriods = new TimePeriodCombiner<TimeRange>().CombinePeriods(curvePeriodDates); // 计算总时段内的间隙 var gaps = new TimeGapCalculator<TimeRange>().GetGaps(combinedPeriods, totalPeriod); // 遍历输出间隙示例 foreach (var gap in gaps) { Console.WriteLine($"间隙:{gap.Start:yyyy-MM-dd HH:mm} 至 {gap.End:yyyy-MM-dd HH:mm}"); }
关键修复点说明
- 排序必须生效:无论是自定义类集合还是TimeRange集合,都要确保按起始时间排序,否则合并时段时会出现逻辑错误。
- 确保时间对象符合库要求:要么让自定义类实现
ITimePeriod接口,要么直接使用库自带的时间范围类,这样合并和间隙计算才能正确工作。 - 简化逻辑:无特殊需求时,直接用库自带的
TimeRange和TimePeriodCollection可减少自定义代码带来的兼容性问题。
内容的提问来源于stack exchange,提问作者user2284452
相关产品推荐
相关产品推荐

