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

时间:2015-09-24 12:27:58

标签: c# linq date-range

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

可能有也可能没有连续日期,但周范围内可能存在一致的差距(即所有星期一,星期三和星期四或所有星期一和星期日,它可以是一周1至7天的任何组合)< / p>

示例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相同,但在中间(星期二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;

2 个答案:

答案 0 :(得分:0)

可能不是您问题的完整答案,因为您可能在后面使用数据库。需要进行大量调整,以便根据自己的使用情况进行调整。

但是这里...我的答案我用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;
    }
}

答案 1 :(得分:0)

LINQ解决方案不值得追求。我不想保留你最终可能用来制作图案的lambda汤。因此,我有点OOP-y。

这里的周就是事情。您看起来要寻找的模式基于周的形状(即,运行的日期)。那么具有相同形状的周可以在期间串联起来。

一周是由Days组成的,我们指出哪些“工作”(正在运行)。这样可以很容易地看出两周是否具有相同的形状。

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;
        }
    }