Linq按时间范围过滤

时间:2014-06-02 09:38:18

标签: c# linq datetime search filter

我正在尝试构建一个在2天之间过滤的Linq查询。

首先,我需要在日期之间进行过滤(即:2013年12月26日至2014年1月26日),在此搜索之后,结果可能会在不同时间之间进行过滤(即:19:00到7之间的记录: 00)。

我已尝试使用此查询,但它不起作用:

orders = CurrentOrders.Where(o => o.ImportedOn >= dateFrom && o.ImportedOn <= dateTo);
orders = orders.Where(o => o.ImportedOn.TimeOfDay >= tFrom && o.ImportedOn.TimeOfDay <= tTo);

tFrom和tTo都是TimeSpan。

有任何帮助吗? 此致

编辑:您好我正在编辑我自己的问题只是为了澄清问题并附上我找到的解决方案(我不确定它是否最有效):

当我说“不起作用”时,我的意思是“没有返回符合标准的记录”。 上一个查询仅适用于同一天之间的时间(即:从15:00到20:00)。问题是时间跨度到第二天,例如从下午19:00到早上7:00。

我建议的解决方案是添加一个简单的if来检查tFrom是否小于tTo,否则在搜索中将日期加1:

if (tFrom < tTo)
{
    orders = orders.Where(o => o.ImportedOn.TimeOfDay >= tFrom && o.ImportedOn.TimeOfDay <= tTo);
}
else
{
    orders = orders.Where(o => o.ImportedOn.TimeOfDay <= tFrom && o.ImportedOn.AddDays(1).TimeOfDay <= tTo);
}

3 个答案:

答案 0 :(得分:1)

例如,当时间范围是晚上9点到凌晨4点时,我认为您的查询不会给您带来结果。假设ImportedOn是晚上7点,你正在检查7 PM是否低于晚上9点,这是正常的,还检查7 PM是否小于凌晨4点,这是错误的。如果你添加一天,我没有看到任何差异因为你只考虑时间。添加日期不会改变时间。

我的建议是在时间从大于时间到(例如下午9点到凌晨4点)时创建两个时间间隔。

我为DateTime创建了一个扩展方法,因此我们可以检查日期是否属于某个时间范围。

public static bool IsInTimeRange(this DateTime obj, DateTime timeRangeFrom, DateTime timeRangeTo)
        {
            TimeSpan time = obj.TimeOfDay, t1From = timeRangeFrom.TimeOfDay, t1To = timeRangeTo.TimeOfDay;

            // if the time from is smaller than the time to, just filter by range
            if (t1From <= t1To)
            {
                return time >= t1From && time <= t1To;
            }

            // time from is greater than time to so two time intervals have to be created: one {timeFrom-12AM) and another one {12AM to timeTo}
            TimeSpan t2From = TimeSpan.MinValue, t2To = t1To;
            t1To = TimeSpan.MaxValue;

            return (time >= t1From && time <= t1To) || (time >= t2From && time <= t2To);
        }

编辑:请注意,没有必要将时间与t2From和t1To进行比较,因为比较始终是真的,但它使代码更容易阅读,因为它明确地检查日期属于两者中的一个间隔。

我还写了这些单元测试:

[TestMethod]
        public void TimeRangeFilter_timeFrom_is_smaller_than_timeTo()
        {
            // arrange
            List<DateTime> dates = new List<DateTime>() 
            {
                DateTime.Today.AddHours(2), // 2 AM
                DateTime.Today.AddHours(9), // 9 AM
                DateTime.Today.AddHours(12), // 12 PM
                DateTime.Today.AddHours(15), // 3 PM
                DateTime.Today.AddHours(18), // 6 PM
                DateTime.Today.AddHours(23).AddMinutes(50), // 11:50 PM
                DateTime.Today, // 0 AM
            };
            // interval: 10 AM to 4 PM
            DateTime timeFrom = DateTime.Today.AddHours(10), timeTo = DateTime.Today.AddHours(16);

            // act
            var datesInPeriod = dates.Where(p => p.IsInTimeRange(timeFrom, timeTo));

            // assert
            Assert.IsFalse(datesInPeriod.Any(p => p.Hour == 2));
            Assert.IsFalse(datesInPeriod.Any(p => p.Hour == 9));
            Assert.IsTrue(datesInPeriod.Any(p => p.Hour == 12));
            Assert.IsTrue(datesInPeriod.Any(p => p.Hour == 15));
            Assert.IsFalse(datesInPeriod.Any(p => p.Hour == 18));
            Assert.IsFalse(datesInPeriod.Any(p => p.Hour == 23));

        }

        [TestMethod]
        public void TimeRangeFilter_timeFrom_is_greater_than_timeTo()
        {
            // arrange
            List<DateTime> dates = new List<DateTime>() 
            {
                DateTime.Today.AddHours(2), // 2 AM
                DateTime.Today.AddHours(9), // 9 AM
                DateTime.Today.AddHours(12), // 12 PM
                DateTime.Today.AddHours(15), // 3 PM
                DateTime.Today.AddHours(18), // 6 PM
                DateTime.Today.AddHours(23).AddMinutes(50), // 11:50 PM
                DateTime.Today, // 0 AM
            };
            // interval: 10 PM to 4 AM
            DateTime timeFrom = DateTime.Today.AddHours(22), timeTo = DateTime.Today.AddHours(4);

            // act
            var datesInPeriod = dates.Where(p => p.IsInTimeRange(timeFrom, timeTo));

            // assert
            Assert.IsTrue(datesInPeriod.Any(p => p.Hour == 2));
            Assert.IsFalse(datesInPeriod.Any(p => p.Hour == 9));
            Assert.IsFalse(datesInPeriod.Any(p => p.Hour == 12));
            Assert.IsFalse(datesInPeriod.Any(p => p.Hour == 15));
            Assert.IsFalse(datesInPeriod.Any(p => p.Hour == 18));
            Assert.IsTrue(datesInPeriod.Any(p => p.Hour == 23));

        }

答案 1 :(得分:0)

ImportedOn.TimeOfDay

只返回一个时间。例如:

            DateTime t = DateTime.Now;
            System.Diagnostics.Debug.WriteLine(t.ToString());
            var t2 = t.TimeOfDay;
            System.Diagnostics.Debug.WriteLine(t2.ToString());

返回:

02.06.2014 11:48:33
11:48:33.6671525

所以你可以比较日期,不需要TimeOfDay。

答案 2 :(得分:0)

Linq to SQL:如何在DateTime字段中查询时间范围

如果您指定在表上选择具有DateTime字段(在此示例中称为DateFiled)的记录,则在两个DateTime值之间,如果您指定:

DateFiled >= dateValue && DateFiled <= dateValue

您选择此值之间的所有记录,但如果您只需要选择这两个日期之间的记录对应于特定时间范围,则需要添加一个计算小时的列。

如果您不想将其添加到数据库,则需要将其添加到模型中,并使用与DateField字段相同的值进行设置。例如:

public async Task<List<MyModel>> GetFilteredResultByDateAndTime
  (DateTime startDate, DateTime endDate)
{
  var result = from mytable in _context.MyTable
               where
               mytable.DateField >= startDate.Date
               && mytable.DateField <= endDate.Date
               select new MyModel
               {
                 DateField = mytable.DateField.Date,
                 DateFieldTime = mytable.DateField,
                 // Other fields of the model
               };

  // Now you can filter the result by the time
  var filteredResult = from r in result
                       where 
                       r.DateFieldTime.TimeOfDay >= startDate.TimeOfDay
                       && r.DateFieldTime.TimeOfDay <= endDate.TimeOfDay
                       select r;
  return await filteredResult.ToListAsync();
}