这似乎是一件简单的事情,但 EF 不支持像
DateTimeOffset.DateTime
或 DateTimeOffset.LocalDateTime
甚至 DateTime.Date
这样的属性。 我试图仅通过 DateTime 组件(即“本地”DateTime)过滤 DateTimeOffset 类型字段,这在普通 sql 中是一件简单的事情:where cast(x as datetime) = '2016-12-14'
。
有一个
DbFunctions.TruncateTime
,但没有对应的TruncateOffset
。
似乎没有任何方法可以将
DateTimeOffset
对象转换或转换为在 linq-to-entities 中工作的普通 DateTime
对象。唯一的转换是从 DateTime
到 DateTimeOffset
的隐式转换,但反之则不然。
您可以使用 DbFunctions.CreateDateTime 来去除偏移量,例如:
...
.Select(e => new MyClass {
MyDate = DbFunctions.CreateDateTime(e.MyDateTimeOffset.Year,
e.MyDateTimeOffset.Month,
e.MyDateTimeOffset.Day,
e.MyDateTimeOffset.Hour,
e.MyDateTimeOffset.Minute,
e.MyDateTimeOffset.Second)
...
正如您可能怀疑的那样,这会创建一些真正糟糕的 SQL,即:
convert (datetime2,right('000' + convert(varchar(255), DATEPART (year, [Extent1].[MyDateTimeOffset])), 4) + '-' + convert(varchar(255), DATEPART (month, [Extent1].[MyDateTimeOffset])) + '-' + convert(varchar(255), DATEPART (day, [Extent1].[MyDateTimeOffset])) + ' ' + convert(varchar(255), DATEPART (hour, [Extent1].[MyDateTimeOffset])) + ':' + convert(varchar(255), DATEPART (minute, [Extent1].[MyDateTimeOffset])) + ':' + str( CAST( DATEPART (second, [Extent1].[MyDateTimeOffset]) AS float), 10, 7), 121) AS [C2]
但是它会给你你想要的。
需要注意的是,这将完全剥离/忽略偏移(按照OP的要求)。
我遇到了与
DbFunctions.TruncateTime
完全相同的问题,并且能够使用 DbFunctions.DiffDays
解决它
这是一个例子:
DbFunctions.DiffDays(DateTimeOffset.Now, x.EventDate) == 0
使用 DbFunctions.DiffDays 时,不考虑偏移量。以下是 MSDN 文档:https://msdn.microsoft.com/en-us/library/dn220092(v=vs.113).aspx
在 EF Core 中,您可以使用
DbFunctions
类。考虑以下几点:
var query = await DbContext.Records
.AsNoTracking()
.OrderByDescending(x => x.Date)
.Where(x => EF.Functions.DateFromParts(x.Date.Year, x.Date.Month, x.Date.Day) >= startDate)
.ToListAsync();
请注意,如果您的日期列是
DateTimeOffset
,您可能需要在查询数据库后在客户端转换这些值,例如将它们转换为本地日期和时间。
另一个有用的功能是对值进行分区。如果您将日期作为 UTC 存储在数据库中,使用
DatimeTimeOffset
列(正如您应该的那样),如何针对不同时区查询数据库?
您可以使用
AtTimeZone
方法。考虑以下示例,我们按日期对记录进行分组,并划分到特定时区:
var query = await DbContext.Records
.AsNoTracking()
.OrderByDescending(x => x.Date)
.GroupBy(x => EF.Functions.DateFromParts(
EF.Functions.AtTimeZone(x.Date, TIME_ZONE_ID).Year,
EF.Functions.AtTimeZone(x.Date, TIME_ZONE_ID).Month,
EF.Functions.AtTimeZone(x.Date, TIME_ZONE_ID).Day))
.OrderByDescending(x => x.Key)
.Select(x => new
{
Date = x.Key,
Records = x.ToList()
})
.ToListAsync();
TIME_ZONE_ID
是有效的时区标识符(例如 Iran Standard Time
)。您可以将任何参数应用于此类查询,并实现分页。但请注意,像这样分组时,您可能无法使用查询拆分,因为它可能会抛出异常,表明您的查询无法转换为 SQL。