修改 看起来创建一个表,其中包含DateTimes按分钟加入,这将是最有意义的。 100年的分钟是〜52M行。由Ticks索引应该使查询运行得非常快。它现在变成了
感谢大家的反馈!
我有一个名为Recurrence的类,如下所示:
public class Recurrence
{
public int Id { get; protected set; }
public DateTime StartDate { get; protected set; }
public DateTime? EndDate { get; protected set; }
public long? RecurrenceInterval { get; protected set; }
}
它是一个实体框架POCO类。我想用这个类做两件事,都是标准的查询运算符。 (这样查询完全在服务器端运行)。
首先,我想创建一个查询,该查询返回从开始日期到结束日期的所有日期,包括给定的重复间隔。迭代函数很简单
for(i=StartDate.Ticks; i<=EndDate.Ticks; i+=RecurrenceInterval)
{
yield return new DateTime(i);
}
Enumerable.Range()将是一个选项,但没有长版本的Range。我认为我唯一的选择是聚合,但我对这个功能仍然不是很强。
最后,一旦我的查询工作,我想从那里返回时间窗口内的值,即在不同的开始和结束日期之间。这很容易使用SkipWhile / TakeWhile。
如果DateTime.Ticks是一个int
,我就可以这样做from recurrence in Recurrences
let range =
Enumerable
.Range(
(int)recurrence.StartDate.Ticks,
recurrence.EndDate.HasValue ? (int)recurrence.EndDate.Value.Ticks : (int)end.Ticks)
.Where(i=>i-(int)recurrence.StartDate.Ticks%(int)recurrence.RecurrenceLength.Value==0)
.SkipWhile(d => d < start.Ticks)
.TakeWhile(d => d <= end.Ticks)
from date in range
select new ScheduledEvent { Date = new DateTime(date) };
我想我需要的是可以通过EF查询执行的LongRange实现。
答案 0 :(得分:2)
您可以创建自己的日期范围方法
public static class EnumerableEx
{
public static IEnumerable<DateTime> DateRange(DateTime startDate, DateTime endDate, TimeSpan intervall)
{
for (DateTime d = startDate; d <= endDate; d += intervall) {
yield return d;
}
}
}
然后用
查询var query =
from recurrence in Recurrences
from date in EnumerableEx.DateRange(recurrence.StartDate,
recurrence.EndDate ?? end,
recurrence.RecurrenceInterval)
select new ScheduledEvent { Date = date };
这假定RecurrenceInterval
被声明为TimeSpan
而end
被声明为DateTime
。
编辑:当你想到时,这个版本会限制服务器端的重复吗?
var query =
from recurrence in Recurrences
where
recurrence.StartDate <= end &&
(recurrence.EndDate != null && recurrence.EndDate.Value >= start ||
recurrence.EndDate == null)
from date in EnumerableEx.DateRange(
recurrence.StartDate,
recurrence.EndDate.HasValue && recurrence.EndDate.Value < end ? recurrence.EndDate.Value : end,
recurrence.RecurrenceInterval)
where (date >= start)
select new ScheduledEvent { Date = date };
此处返回的重复已经考虑了start
和end
日期,因此不会返回过时的重复。 EnumerableEx.DateRange
对查询的第一部分没有影响。
答案 1 :(得分:2)
这是产生递归点和指定子区间的交集的函数:
public class Recurrence
{
public int Id { get; protected set; }
public DateTime StartDate { get; protected set; }
public DateTime? EndDate { get; protected set; }
public long? RecurrenceInterval { get; protected set; }
// returns the set of DateTimes within [subStart, subEnd] that are
// of the form StartDate + k*RecurrenceInterval, where k is an Integer
public IEnumerable<DateTime> GetBetween(DateTime subStart, DateTime subEnd)
{
long stride = RecurrenceInterval ?? 1;
if (stride < 1)
throw new ArgumentException("Need a positive recurrence stride");
long realStart, realEnd;
// figure out where we really need to start
if (StartDate >= subStart)
realStart = StartDate.Ticks;
else
{
long rem = subStart.Ticks % stride;
if (rem == 0)
realStart = subStart.Ticks;
else
// break off the incomplete stride and add a full one
realStart = subStart.Ticks - rem + stride;
}
// figure out where we really need to stop
if (EndDate <= subEnd)
// we know EndDate has a value. Null can't be "less than" something
realEnd = EndDate.Value.Ticks;
else
{
long rem = subEnd.Ticks % stride;
// break off any incomplete stride
realEnd = subEnd.Ticks - rem;
}
if (realEnd < realStart)
yield break; // the intersection is empty
// now yield all the results in the intersection of the sets
for (long t = realStart; t <= realEnd; t += stride)
yield return new DateTime(t);
}
}