如何在一段时间内获得日期范围的差距

时间:2015-01-09 02:00:00

标签: c# linq date range

我的初始和最终日期范围= 2015年1月1日 - 2015年1月30日

我有这些日期范围代表不可用日期。

1/5/2015 - 1/10/2015
1/15/2015 - 1/20/2015
1/22/2015 - 1/28/2015

我想要这个输出,主要是主要范围的可用日期:

A: 1/1/2015 - 1/4/2015
B: 1/11/2015 - 1/14/2015
C: 1/21/2015 - 1/21/2015
D: 1/29/2015 - 1/30/2015

我尝试生成这样的顺序日期范围,以便使用Except()获取异常日期,但我认为这使事情变得复杂。

 //dtStartDate = 1/1/2015
 //dtEndDate = 1/30/2015  
 var days = (int)(dtEndDate - dtStartDate).TotalDays + 1;
 var completeSeq = Enumerable.Range(0, days).Select(x => dtStartDate.AddDays(x)).ToArray();

如何从一段时间内获得日期范围的差距。

我换句话说如何从这张照片中获得A,B,C和D

http://www.tiikoni.com/tis/view/?id=ebe851c

如果这些日期重叠,则不得仅考虑它们的差距。

---------- ----------- UPDATE

我想如果我这样做:

 var range = Enumerable.Range(0, (int)(1/10/2015 - 1/5/2015).TotalDays + 1).Select(i => 1/5/2015.AddDays(i));
 var missing = completeSeq.Except(range).ToArray();

对于每个日期范围,我将排除每个日期范围,但仍然无法获得差距!

3 个答案:

答案 0 :(得分:2)

我今天早上看到了你的问题并且非常喜欢它,但整天都很忙。所以,有机会玩你的问题,相信我,我很喜欢它。这是我的代码: -

DateTime startDate = new DateTime(2015, 1, 1);
DateTime endDate = new DateTime(2015, 1, 30);
int totalDays = (int)(endDate - startDate).TotalDays + 1;
availability.Add(new Availability { StartDate = endDate, EndDate = endDate });

var result = from x in Enumerable.Range(0, totalDays)
            let d = startDate.AddDays(x)
            from a in availability.Select((v, i) => new { Value = v, Index = i })
            where (a.Index == availability.Count - 1 ? 
                     d <= a.Value.StartDate : d < a.Value.StartDate)
            && (a.Index != 0 ? d > availability[a.Index - 1].EndDate : true)
            group new { d, a } by a.Value.StartDate into g
            select new
            {
                 AvailableDates = String.Format("{0} - {1}",g.Min(x => x.d),
                                                            g.Max(x => x.d))
            };

这绝对需要解释,所以这里是: -

第1步:使用Enumerable.Range在1月1日至1月30日期间创建一系列日期 第2步:由于在第二个不可用的日期范围之后,我们需要限制从上一个endate选择的日期到当前对象startdate,我已计算index以便我们可以访问最后一个ENDDATE。
第3步:获得索引后,我们需要做的就是过滤除第一个日期范围之外的日期,因为在这种情况下我们没有最后一个对象。
第4步:对于最后一项,因为我们没有最大范围,我将endDate添加到我们的不可用列表中(希望这是有意义的)。

以下是Working Fiddle,如果您感到困惑,只需删除group by和其他过滤器并进行调试,看看结果输出看起来相当简单:)

答案 1 :(得分:1)

using System;
using System.Collections.Generic;
using System.Linq;
public static class Program {
    public static void Main() {
        Tuple<DateTime,DateTime> range=Tuple.Create(new DateTime(2015,1,1),new DateTime(2015,1,30));
        Tuple<DateTime,DateTime>[] exclude=new[] {
            Tuple.Create(new DateTime(2015,1,5),new DateTime(2015,1,10)),
            Tuple.Create(new DateTime(2015,1,15),new DateTime(2015,1,20)),
            Tuple.Create(new DateTime(2015,1,22),new DateTime(2015,1,28))
        };
        foreach(Tuple<DateTime,DateTime> r in ExcludeIntervals(range,exclude)) {
            Console.WriteLine("{0} - {1}",r.Item1,r.Item2);
        }
    }
    public static IEnumerable<Tuple<DateTime,DateTime>> ExcludeIntervals(Tuple<DateTime,DateTime> range,IEnumerable<Tuple<DateTime,DateTime>> exclude) {
        IEnumerable<Tuple<DateTime,bool>> dates=
            new[] { Tuple.Create(range.Item1.AddDays(-1),true),Tuple.Create(range.Item2.AddDays(1),false) }.
            Concat(exclude.SelectMany(r => new[] { Tuple.Create(r.Item1,false),Tuple.Create(r.Item2,true) })).
            OrderBy(d => d.Item1).ThenBy(d => d.Item2); //Get ordered list of time points where availability can change.
        DateTime firstFreeDate=default(DateTime);
        int count=1; //Count of unavailability intervals what is currently active. Start from 1 to threat as unavailable before range starts.
        foreach(Tuple<DateTime,bool> date in dates) {
            if(date.Item2) { //false - start of unavailability interval. true - end of unavailability interval.
                if(--count==0) { //Become available.
                    firstFreeDate=date.Item1.AddDays(1);
                }
            } else {
                if(++count==1) { //Become unavailable.
                    DateTime lastFreeDate=date.Item1.AddDays(-1);
                    if(lastFreeDate>=firstFreeDate) { //If next unavailability starts right after previous ended, then no gap.
                        yield return Tuple.Create(firstFreeDate,lastFreeDate);
                    }
                }
            }
        }
    }
}

ideone.com

答案 2 :(得分:1)

得到了一点点oopy ......

public class DateRange
    {
        public DateTime Start { get; set; }
        public DateTime End { get; set; }

        public bool HasStart
        {
            get { return Start != DateTime.MinValue; }
        }
        public bool IsInRange(DateTime date)
        {
            return (date >= this.Start && date <= this.End);
        }

        public List<DateRange> GetAvailableDates(DateRange excludedRange)
        {
            return GetAvailableDates(new List<DateRange>(){excludedRange});
        }

        public List<DateRange> GetAvailableDates(List<DateRange> excludedRanges)
        {
            if (excludedRanges == null)
            {
                return new List<DateRange>() { this };
            }
            var list = new List<DateRange>();
            var aRange = new DateRange();
            var date = this.Start;
            while (date <= this.End)
            {
                bool isInARange = excludedRanges.Any(er => er.HasStart && er.IsInRange(date));
                if (!isInARange)
                {
                    if (!aRange.HasStart)
                    {
                        aRange.Start = date;
                    }
                    aRange.End = date;
                }
                else
                {
                    if (aRange.HasStart)
                    {
                        list.Add(aRange);
                        aRange = new DateRange();
                    }
                }
                date = date.AddDays(1);
            }
            if (aRange.HasStart)
            {
                list.Add(aRange);
            }
            return list;
            }
}