从日期列表中获取日期范围列表

Get a list of date Ranges from a list Of dates

我想从日期列表中获取日期范围列表(含天数)。

可能有也可能没有连续的日期,但周范围内可能有一致的间隔(即所有星期一、星期三和星期四或所有星期一和星期日,它可以是一周中 1 到 7 天的任意组合)

示例 1 - 跨越 2 个月且都在星期一、星期三或星期五的日期列表

ID  Date        Day
31  2016-02-01  Monday
31  2016-02-03  Wednesday
31  2016-02-05  Friday
31  2016-02-08  Monday
31  2016-02-10  Wednesday
31  2016-02-12  Friday
31  2016-02-15  Monday
31  2016-02-17  Wednesday
31  2016-02-19  Friday
31  2016-02-22  Monday
31  2016-02-24  Wednesday
31  2016-02-26  Friday
31  2016-02-29  Monday
31  2016-03-02  Wednesday
31  2016-03-04  Friday
31  2016-03-07  Monday
31  2016-03-09  Wednesday
31  2016-03-11  Friday

目标 - 确定所有日期都遵循一种模式 - 所有日期都在同一天,没有间隔

期望的输出

ID                       31 
FROMDATE                 01/02/2016
TODATE                   11/03/2016
OPERATINGDAY_MONDAY      TRUE
OPERATINGDAY_TUESDAY     FALSE
OPERATINGDAY_WEDNESDAY   TRUE
OPERATINGDAY_THURSDAY    FALSE
OPERATINGDAY_FRIDAY      TRUE
OPERATINGDAY_SATURDAY    FALSE
OPERATINGDAY_SUNDAY      FALSE

示例 2 - 与上面相同,但中间有一个间隔(不是 19 日星期五)- 分成 2 个日期范围

ID  Date        Day
31  2016-02-01  Monday
31  2016-02-03  Wednesday
31  2016-02-05  Friday
31  2016-02-08  Monday
31  2016-02-10  Wednesday
31  2016-02-12  Friday
31  2016-02-15  Monday
31  2016-02-17  Wednesday
31  2016-02-22  Monday
31  2016-02-24  Wednesday
31  2016-02-26  Friday
31  2016-02-29  Monday
31  2016-03-02  Wednesday
31  2016-03-04  Friday
31  2016-03-07  Monday
31  2016-03-09  Wednesday
31  2016-03-11  Friday

期望的输出是这样的对象

ID                       31 
FROMDATE                 01/02/2016
TODATE                   17/02/2016
OPERATINGDAY_MONDAY      TRUE
OPERATINGDAY_TUESDAY     FALSE
OPERATINGDAY_WEDNESDAY   TRUE
OPERATINGDAY_THURSDAY    FALSE
OPERATINGDAY_FRIDAY      TRUE
OPERATINGDAY_SATURDAY    FALSE
OPERATINGDAY_SUNDAY      FALSE

ID                       31 
FROMDATE                 22/02/2016
TODATE                   11/03/2016
OPERATINGDAY_MONDAY      TRUE
OPERATINGDAY_TUESDAY     FALSE
OPERATINGDAY_WEDNESDAY   TRUE
OPERATINGDAY_THURSDAY    FALSE
OPERATINGDAY_FRIDAY      TRUE
OPERATINGDAY_SATURDAY    FALSE
OPERATINGDAY_SUNDAY      FALSE

示例 3 - 与示例 1 相同,但中间有一个新日期 date/day(星期二 16 日)

ID  Date        Day
31  2016-02-01  Monday
31  2016-02-03  Wednesday
31  2016-02-05  Friday
31  2016-02-08  Monday
31  2016-02-10  Wednesday
31  2016-02-12  Friday
31  2016-02-15  Monday
31  2016-02-16  Tuesday
31  2016-02-17  Wednesday
31  2016-02-22  Monday
31  2016-02-24  Wednesday
31  2016-02-26  Friday
31  2016-02-29  Monday
31  2016-03-02  Wednesday
31  2016-03-04  Friday
31  2016-03-07  Monday
31  2016-03-09  Wednesday
31  2016-03-11  Friday

期望的输出是这样的对象

ID                       31 
FROMDATE                 01/02/2016
TODATE                   15/02/2016
OPERATINGDAY_MONDAY      TRUE
OPERATINGDAY_TUESDAY     FALSE
OPERATINGDAY_WEDNESDAY   TRUE
OPERATINGDAY_THURSDAY    FALSE
OPERATINGDAY_FRIDAY      TRUE
OPERATINGDAY_SATURDAY    FALSE
OPERATINGDAY_SUNDAY      FALSE

ID                       31 
FROMDATE                 16/02/2016
TODATE                   16/02/2016
OPERATINGDAY_MONDAY      FALSE
OPERATINGDAY_TUESDAY     TRUE
OPERATINGDAY_WEDNESDAY   FALSE
OPERATINGDAY_THURSDAY    FALSE
OPERATINGDAY_FRIDAY      FALSE
OPERATINGDAY_SATURDAY    FALSE
OPERATINGDAY_SUNDAY      FALSE

ID                       31 
FROMDATE                 17/02/2016
TODATE                   11/03/2016
OPERATINGDAY_MONDAY      TRUE
OPERATINGDAY_TUESDAY     FALSE
OPERATINGDAY_WEDNESDAY   TRUE
OPERATINGDAY_THURSDAY    FALSE
OPERATINGDAY_FRIDAY      TRUE
OPERATINGDAY_SATURDAY    FALSE
OPERATINGDAY_SUNDAY      FALSE

关于如何攻击这个问题的正确方向的观点会很棒。最好是 linq.

最终目标是以连贯的方式显示数据

更新:这是我目前正在使用的代码。我正在处理连续的日子和非连续的日子,但我不确定从哪里开始遵循某种模式的日期。毫无疑问,我连续几天都处理错了。

foreach (var o in option.OptionPrices)
                {
                    var dateList = o.Dates.Select(d => d.Date).OrderBy(d => d.Date);
                    maxDate = (from d in dateList select d.Date).Max();
                    minDate = (from d in dateList select d.Date).Min();

                    if ((maxDate - minDate).Days + 1 == dateList.Count()) // runs everyday specified.
                    {
                        dateRange.Start = minDate;
                        dateRange.End = maxDate;
                        dateRange.OptionPriceid = o.OptionPriceId;
                        dateRange.OptionPriceName = o.Name;                        
                        dateRange.DayNames.Add("Monday");
                        dateRange.DayNames.Add("Tuesday");
                        dateRange.DayNames.Add("Wednesday");
                        dateRange.DayNames.Add("Thursday");
                        dateRange.DayNames.Add("Friday");
                        dateRange.DayNames.Add("Saturday");
                        dateRange.DayNames.Add("Sunday");

                        Pricing pricing = new Pricing
                        {
                            OptionId = option.OptionId,
                            OptionName = option.Name,
                            OptionPriceName = dateRange.OptionPriceName,
                            Fromdate = dateRange.Start.ToShortDateString(),
                            Todate = dateRange.End.ToShortDateString(),
                            AdultPrice = o.AdultPrice,
                            ChildPrice = o.ChildPrice,
                            InfantPrice = o.InfantPrice,
                            Operatingday_Monday = dateRange.DayNames.Contains("Monday") ? "x" : "-",
                            Operatingday_Tuesday = dateRange.DayNames.Contains("Tuesday") ? "x" : "-",
                            Operatingday_Wednesday = dateRange.DayNames.Contains("Wednesday") ? "x" : "-",
                            Operatingday_Thursday = dateRange.DayNames.Contains("Thursday") ? "x" : "-",
                            Operatingday_Friday = dateRange.DayNames.Contains("Friday") ? "x" : "-",
                            Operatingday_Saturday = dateRange.DayNames.Contains("Saturday") ? "x" : "-",
                            Operatingday_Sunday = dateRange.DayNames.Contains("Sunday") ? "x" : "-"
                        };

                        pricingModel.Add(pricing);

                        dateRange = new DateRange { DayNames = new List<string>() };                        
                        continue;
                    }

    // if we get to here we have non consecutive  dates 
                    foreach (var optionPriceDate in o.Dates.Select(d=>d.Date).OrderBy(d=>d.Date))
                    {                                                
                        currentDate = optionPriceDate;
                        if (dateRange.Start == DateTime.MinValue) // new range
                        {
                            dateRange.Start = currentDate; 
                            dateRange.OptionPriceid = o.OptionPriceId;
                            dateRange.OptionPriceName = o.Name;
                            if (!dateRange.DayNames.Contains(currentDate.DayOfWeek.ToString()))
                            {
                                dateRange.DayNames.Add(currentDate.DayOfWeek.ToString());
                            }                            
                            maxDate  = (from d in o.Dates select d.Date).Max(); 
                        }

                        if (currentDate == maxDate && (maxDate-lastDate).Days == 1 ) //last consecative day
                        {
                            dateRange.End = currentDate;
                            if (!dateRange.DayNames.Contains(currentDate.DayOfWeek.ToString()))
                            {
                                dateRange.DayNames.Add(currentDate.DayOfWeek.ToString());
                            }                     
                            Pricing pricing = new Pricing
                            {
                                OptionId = option.OptionId,
                                OptionName = option.Name,
                                OptionPriceName = dateRange.OptionPriceName,
                                Fromdate = dateRange.Start.ToShortDateString(),
                                Todate = dateRange.End.ToShortDateString(),
                                AdultPrice = o.AdultPrice,
                                ChildPrice = o.ChildPrice,
                                InfantPrice = o.InfantPrice,
                                Operatingday_Monday = dateRange.DayNames.Contains("Monday") ? "x" : "-",
                                Operatingday_Tuesday = dateRange.DayNames.Contains("Tuesday") ? "x" : "-",
                                Operatingday_Wednesday = dateRange.DayNames.Contains("Wednesday") ? "x" : "-",
                                Operatingday_Thursday = dateRange.DayNames.Contains("Thursday") ? "x" : "-",
                                Operatingday_Friday = dateRange.DayNames.Contains("Friday") ? "x" : "-",
                                Operatingday_Saturday = dateRange.DayNames.Contains("Saturday") ? "x" : "-",
                                Operatingday_Sunday = dateRange.DayNames.Contains("Sunday") ? "x" : "-"
                            };
                            pricingModel.Add(pricing);

                            // END of Price band - Rset everything
                            dateRange = new DateRange
                            {
                                Start = new DateTime(),
                                End = new DateTime(),
                                DayNames = new List<string>()
                            };
                            lastDate = DateTime.MinValue;
                            continue;
                        }



                        // Check to see if there is a gap
                        if ((currentDate.AddDays(-1) != lastDate) && lastDate != DateTime.MinValue)
                        {
                            dateRange.End = lastDate;

                            Pricing pricing = new Pricing
                            {
                                OptionId = option.OptionId,
                                OptionName = option.Name,
                                OptionPriceName = dateRange.OptionPriceName,
                                Fromdate = dateRange.Start.ToShortDateString(),
                                Todate = dateRange.End.ToShortDateString(),
                                AdultPrice = o.AdultPrice,
                                ChildPrice = o.ChildPrice,
                                InfantPrice = o.InfantPrice,
                                Operatingday_Monday = dateRange.DayNames.Contains("Monday") ? "x" : "-",
                                Operatingday_Tuesday = dateRange.DayNames.Contains("Tuesday") ? "x" : "-",
                                Operatingday_Wednesday = dateRange.DayNames.Contains("Wednesday") ? "x" : "-",
                                Operatingday_Thursday = dateRange.DayNames.Contains("Thursday") ? "x" : "-",
                                Operatingday_Friday = dateRange.DayNames.Contains("Friday") ? "x" : "-",
                                Operatingday_Saturday = dateRange.DayNames.Contains("Saturday") ? "x" : "-",
                                Operatingday_Sunday = dateRange.DayNames.Contains("Sunday") ? "x" : "-"
                            };
                            pricingModel.Add(pricing);                    

                            // Start New dateRange
                            dateRange = new DateRange
                            {
                                Start = currentDate,
                                End = new DateTime(),
                                OptionPriceid = o.OptionPriceId,
                                OptionPriceName = o.Name,
                                DayNames = new List<string>()
                            };
                            dateRange.DayNames.Add(currentDate.DayOfWeek.ToString());
                            lastDate = currentDate;
                            continue;                                                                           
                        }
                        if (!dateRange.DayNames.Contains(currentDate.DayOfWeek.ToString()))
                        {
                            dateRange.DayNames.Add(currentDate.DayOfWeek.ToString());
                        }   
                        lastDate = currentDate;
                    }                                        
                }
                PricingList = pricingModel;

可能不是您问题的完整答案,因为您可能在后面使用数据库。需要进行如此多的调整才能使其正确适合您自己的使用。

但是这里...我使用 LinqPad 写的答案只是为了好玩和喝啤酒 ;-)(OP 没有具体说明 Linq2Sql)

少解释。根据特定日历和周开始算法按周数对日期进行分组。然后从每个小组建立那一周的结果。

此示例仅显示部分答案,因为您仍然需要迭代每个结果并确定是否需要合并在操作日内相等的连续记录。

void Main()
{

    var data = new [] { 
        new Record(31, DateTime.Parse("2016-02-01")),
        new Record(31, DateTime.Parse("2016-02-03")),
        new Record(31, DateTime.Parse("2016-02-05")),
        new Record(31, DateTime.Parse("2016-02-08")),
        new Record(31, DateTime.Parse("2016-02-10")),
        new Record(31, DateTime.Parse("2016-02-12")),
        new Record(31, DateTime.Parse("2016-02-15")),
        new Record(31, DateTime.Parse("2016-02-16")),
        new Record(31, DateTime.Parse("2016-02-17")),
        new Record(31, DateTime.Parse("2016-02-22")),
        new Record(31, DateTime.Parse("2016-02-24")),
        new Record(31, DateTime.Parse("2016-02-26")),
        new Record(31, DateTime.Parse("2016-02-29")),
        new Record(31, DateTime.Parse("2016-03-02")),
        new Record(31, DateTime.Parse("2016-03-04")),
        new Record(31, DateTime.Parse("2016-03-07")),
        new Record(31, DateTime.Parse("2016-03-09")),
        new Record(31, DateTime.Parse("2016-03-11"))
    };

    (
        from rec in data
        group rec by new { ID = rec.Id,  WeekNum = new GregorianCalendar().GetWeekOfYear(rec.Date, CalendarWeekRule.FirstDay, DayOfWeek.Monday)} into g
        select new { 
            ID = g.Key.ID, 
            FromDate = g.First().Date, 
            LastDate = g.Last().Date,
            OPERATINGDAY_MONDAY = (g.FirstOrDefault(x=>x.Date.DayOfWeek == DayOfWeek.Monday) != null),
            OPERATINGDAY_TUESDAY = (g.FirstOrDefault(x=>x.Date.DayOfWeek == DayOfWeek.Tuesday) != null),
            OPERATINGDAY_WEDNESDAY = (g.FirstOrDefault(x=>x.Date.DayOfWeek == DayOfWeek.Wednesday) != null),
            OPERATINGDAY_THURSDAY = (g.FirstOrDefault(x=>x.Date.DayOfWeek == DayOfWeek.Thursday) != null),
            OPERATINGDAY_FRIDAY = (g.FirstOrDefault(x=>x.Date.DayOfWeek == DayOfWeek.Friday) != null),
            OPERATINGDAY_SATURDAY = (g.FirstOrDefault(x=>x.Date.DayOfWeek == DayOfWeek.Saturday) != null),
            OPERATINGDAY_SUNDAY = (g.FirstOrDefault(x=>x.Date.DayOfWeek == DayOfWeek.Sunday) != null),
        }
    ).Dump();
}

// Define other methods and classes here
class Record
{
    public int Id {get;set;}
    public DateTime Date {get;set;}
    public string Day {get;set;}

    public Record (int id, DateTime date)
    {
        Id = id;
        Date = date;
    }
}

LINQ 解决方案不值得追求。我不想维护您最终可能构建的任何 lambda 汤以提取模式。因此,我有点 OOP-y。

这就是周。您似乎正在寻找的模式是基于周的形状(即运行的日子)。然后可以将具有相同形状的周串成周期。

一周由天组成,我们指出哪些天 "work"(正在运行)。这样可以很容易地看出两周的形状是否相同。

public class Week
{
    public DateTime StartDate { get { return Days.Keys.FirstOrDefault(); } }
    public DateTime EndDate { get { return Days.Keys.LastOrDefault(); } }
    public Dictionary<DateTime, bool> Days { get; private set; }

    public Week(DateTime startDate)
    {
        if (startDate.DayOfWeek != DayOfWeek.Monday)
        {
            throw  new Exception("Week must start on Monday");
        }
        Days = new Dictionary<DateTime, bool>();
        for (int day = 0; day < 7; day++)
        {
            Days.Add(startDate.AddDays(day), false);
        }
    }
    public int Shape
    {
        get { return Days.Where(d => d.Value).Sum(d => (int)Math.Pow(2, (int)d.Key.DayOfWeek)); }
    }

    public bool AreAlike(Week otherWeek)
    {
        return this.Shape == otherWeek.Shape;
    }
    public void SetWorkDays(List<DateTime> workDays)
    {
        foreach (var workDay in workDays)
        {
            Days[Days.Keys.First(d => d.DayOfWeek == workDay.DayOfWeek)] = true;
        }
    }

    public void SetWorkDay(DayOfWeek dayOfWeek, bool work = true)
    {
        Days[Days.Keys.First(d => d.DayOfWeek == dayOfWeek)] = work;
    }
}

然后周列表可以变成包含具有相同形状的连续周的周期列表。

 public class Period
    {
        public List<Week> Weeks { get; private set; }

        public Period(Week week)
        {
            Weeks = new List<Week>(new[]{week});
        }

        public bool AddWeek(Week week)
        {
            if (!Weeks.First().AreAlike(week))
            {
                return false;
            }
            Weeks.Add(week);
            return true;
        }

        public DateTime StartDate 
        {
            get { return Weeks.First().StartDate; }
        }

        public DateTime EndDate
        {
            get { return Weeks.Last().EndDate; }
        }

        public Dictionary<DayOfWeek, bool> Display
        {
            get { return Weeks.First().Days.ToDictionary(d => d.Key.DayOfWeek, d => d.Value); }

        }
    }

这是一个从周列表中构建周期的工厂...

public class PeriodFactory
    {
        public List<Period> GetPeriodsOfLikeWeeks(List<Week> weeks)
        {
            var periods = new List<Period>();
            Period currentPeriod = null;
            foreach (var week in weeks)
            {
                if (currentPeriod == null)
                {
                    currentPeriod = new Period(week);
                    periods.Add(currentPeriod);
                    continue;
                }
                if (!currentPeriod.AddWeek(week))
                {
                    currentPeriod = new Period(week);
                    periods.Add(currentPeriod);
                }
            }
            return periods;
        }
    }

为了好玩,可以从您的源数据中获取 WeekFactory。

public class WeekFactory
    {
        public List<Week> BuildWeeksFromWorkDays(List<DateTime> workDays)
        {
            workDays = workDays.OrderBy(w => w).ToList();
            var startDate = workDays.First();
            if (startDate.DayOfWeek == DayOfWeek.Sunday)
            {
                startDate = startDate.AddDays(-6);
            }
            else
            {
                startDate = startDate.AddDays((startDate.DayOfWeek - DayOfWeek.Monday) * -1);
            }
            var weeks = new List<Week>();
            while (startDate <= workDays.Last())
            {
                var week = new Week(startDate);
                week.SetWorkDays( workDays.Where(d => d.Date >= startDate && d.Date < startDate.AddDays(7)).ToList());
                weeks.Add(week);
                startDate = startDate.AddDays(7);
            }
            return weeks;
        }
    }