【问题标题】:Combine Time Ranges合并时间范围
【发布时间】:2012-10-29 21:52:16
【问题描述】:

我目前正在开发一个需要组合大量时间范围的 C# 程序。对于每个范围,我都有开始和结束时间。我发现了一个例子,这是在 Ruby 中完成的,但不是在 C# 中完成的。我基本上是在寻找时间范围联盟。我觉得可能有一种方法可以使用 linq 来做到这一点,但我想不出它。有什么想法吗?

例如

开始时间:1:30 结束时间:2:00

开始时间:1:45 结束时间:2:30

开始时间:3:00 结束时间:5:00

开始时间:4:00 结束时间:4:30

开始时间:4:45 结束时间:5:30

这组时间会回来

开始时间:1:30 结束时间:2:30

开始时间:3:00 结束时间:5:30

【问题讨论】:

标签: c# datetime union


【解决方案1】:

你可以看看这个支持TimeRanges和交集方法的项目:

http://www.codeproject.com/Articles/168662/Time-Period-Library-for-NET

【讨论】:

    【解决方案2】:

    这看起来很有趣,所以我开始编写一些代码。

    public class TimeRanges
    {
        private List<TimeRange> _mergedTimeRanges = new List<TimeRange>();
    
        public void Add(TimeRange timeRange)
        {
            if(!_mergedTimeRanges.Any(x=>x.IsOverLap(timeRange)))
            {
                _mergedTimeRanges.Add(timeRange);
                return;
            }
            while (_mergedTimeRanges.Any(x => x.IsOverLap(timeRange) && x!=timeRange))
            {
                TimeRange toMergeRange = _mergedTimeRanges.First(x => x.IsOverLap(timeRange));
                toMergeRange.Merge(timeRange);
                timeRange = toMergeRange;
            }
        }
    
        public IEnumerable<TimeRange> GetMergedRanges()
        {
            return _mergedTimeRanges;
        }
    }
    
    public class TimeRange
    {
        public DateTime Start { get; private set; }
        public DateTime End { get; private set; }
        public TimeRange(DateTime start, DateTime end)
        {
            if (start >= end)
                throw new ArgumentException("Invalid time range, end must be later than start");
            Start = start;
            End = end;
        }
    
        public void Merge(TimeRange timeRange)
        {
            if (!IsOverLap(timeRange))
                throw new ArgumentException("Cannot merge timeranges that don't overlap", "timeRange");
            if (End < timeRange.End)
                End = timeRange.End;
            if (timeRange.Start < Start)
                Start = timeRange.Start;
        }
    
        public bool IsOverLap(TimeRange timeRange)
        {
            if (timeRange.End < Start)
                return false;
            if (timeRange.Start > End)
                return false;
            return true;
        }
    
        public bool Equals(TimeRange other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return other.Start.Equals(Start) && other.End.Equals(End);
        }
    
        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != typeof (TimeRange)) return false;
            return Equals((TimeRange) obj);
        }
    
        public override int GetHashCode()
        {
            unchecked
            {
                return (Start.GetHashCode()*397) ^ End.GetHashCode();
            }
        }
    }
    

    如果有人有兴趣,我有几个测试

    【讨论】:

      【解决方案3】:

      Johan Larsson 的回答对我帮助很大。 我使用了它,并在以下情况下遇到了无限循环,所以我想我会做出贡献。

      开始时间:1:30 结束时间:2:00

      开始时间:2:45 结束时间:4:00

      开始时间:1:45 结束时间:3:00 (与前两个范围重叠)

      所以这是我想出的浓缩版:

      private static IEnumerable<TimeRange> MergeTimeRanges(IEnumerable<TimeRange> ranges)
      {
          var mergedRanges = new List<TimeRange>();
      
          foreach (var range in ranges)
          {
              var overlapping = mergedRanges.Where(r => !(range.End < r.Start) && !(range.Start> r.End)).ToArray();
              if (overlapping.Length == 0)
              {
                  mergedRanges.Add(range);
              }
              else
              {
                  // add a new range made up of the overlapping ranges plus the new range, then delete the ovelapping ranges
                  mergedRanges.Add(new TimeRange { Start = Math.Min(range.Start, overlapping.Min(r => r.Start)), End = Math.Max(range.End, overlapping.Max(r => r.End)) });
                  foreach (var r in overlapping)
                      mergedRanges.Remove(r);
              }
          }
      
          return mergedRanges;
      }
      

      注意:再想一想,可以通过对初始范围 (?) 进行排序来避免无限循环问题。不确定。

      【讨论】:

        猜你喜欢
        • 2017-02-10
        • 1970-01-01
        • 2016-08-22
        • 1970-01-01
        • 1970-01-01
        • 2018-12-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多