获取间隔时间和日期范围
编辑 它看起来像创建一个表格,以分钟为单位保存DateTimes以加入反对会最有意义。 100年的价值是〜52M行。由Ticks索引应该使查询运行得非常快。现在变成获取间隔时间和日期范围
感谢大家的反馈!
我有一个叫复发类,看起来像这样:
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。我在想我在这里唯一的选择是Aggregate,但我仍然不是那么强大。
最后,一旦我有这个查询工作,我想要返回那些在时间窗口内,即在不同的开始日期和结束日期之间的值。这很容易使用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查询。
下面是产生复发点的交叉点和指定的子区间的功能:
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);
}
}
为了便于说明,当“很长?A CodeGnome 2012-01-31 19:03:31
结合你的算法与奥利维尔的防守边缘情况。 – 2012-01-31 19:36:29
这两个很好的答案。这是更多的通过算法,尽管谢谢! – 2012-02-02 19:30:32
你可以创建自己的日期范围方法
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
对查询的第一部分没有影响。
我添加了一个应该由查询提供程序进行转换的版本,因为它只使用where子句中的正常比较,并且在此处不使用日期范围枚举。 – 2012-01-31 19:25:35
我明白你在说什么。一旦我们限制了范围,选择客户端应该是微不足道的。就像我所说的那样,它适用于超过一天的时间间隔。大部分时间窗口也将持续一周。继YAGNI之后,如果性能成为问题,我们将重新审视算法。 – 2012-01-31 19:26:22
对EF的查询最终将转换为SQL语句。你不能期望更多的这个SQL语句,而不是包含适当的WHERE子句。每个补充逻辑都必须在返回的记录上执行(映射到'Recurrence')。 (db)服务器唯一可以做的就是执行查询。 – 2012-01-31 19:31:37
这似乎更适合于每日/每小时的基础上,而不是每滴答返回日期。你如何使用这个? – Magnus 2012-01-31 17:53:23
我认为,即使你能得到你想要的东西,你也会回过头去问如何提高它的性能。当第一步很容易,第二步很难,这可能意味着第一步走在错误的道路上。 – 2012-01-31 17:58:21
你应该考虑使用一个现有的Recurrence库,例如[Quartz.NET](http://quartznet.sourceforge.net/)或[iCalendar](http://sourceforge.net/projects/dday-ical/) – Magnus 2012-01-31 18:00:44