我相信你对时区,特别是偏移量这个部分感到困惑。
MongoDb总是保存UTC时间的日期时间。
因此,当您查看MongoDB中的日期时间时,您必须始终考虑到与本地时区的偏移量。
您将始终以本地时区发送日期。在持久化之前,Mongo C#驱动程序会将时间从本地时间转换为UTC时间。
例如
当我保存文档并使用
CreatedOn = 2017-04-05 15:21:23.234
(美国/芝加哥的本地时区)时,
但是当您查看数据库中的文档时,您将看到
ISODate("2017-04-05T20:21:23.234Z")
,即UTC偏移量为-5小时的本地时间。
[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
指示驱动程序在将BSON反序列化回POCO时将时间从UTC转换为本地时间。
以下是说明行为的测试用例。
代码:class Program
{
static void Main(string[] args)
{
var mongo = new MongoClient("mongodb://localhost:27017/test");
var db = mongo.GetDatabase("test");
db.DropCollection("students");
db.CreateCollection("students");
var collection = db.GetCollection<Student>("students");
var today = DateTime.Now;
var yesterday = today.AddDays(-1);
collection.InsertMany(new[]
{
new Student{Description = "today", CreatedOn = today},
new Student{Description = "yesterday", CreatedOn = yesterday},
}
);
var filterBuilder1 = Builders<Student>.Filter;
var filter1 = filterBuilder1.Eq(x => x.CreatedOn, today);
List<Student> searchResult1 = collection.Find(filter1).ToList();
Console.Write(searchResult1.Count == 1);
var filterBuilder2 = Builders<Student>.Filter;
var filter2 = filterBuilder2.Eq(x => x.CreatedOn, yesterday);
List<Student> searchResult2 = collection.Find(filter2).ToList();
Console.Write(searchResult2.Count == 1);
}
}
public class Student
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
public DateTime CreatedOn { get; set; }
public string Description { get; set; }
}
集合:(通过 mongo shell 查看)
{
"_id" : ObjectId("58e559c76d3a9d2cb0449d84"),
"CreatedOn" : ISODate("2017-04-04T20:21:23.234Z"),
"Description" : "yesterday"
}
{
"_id" : ObjectId("58e559c76d3a9d2cb0449d85"),
"CreatedOn" : ISODate("2017-04-05T20:21:23.234Z"),
"Description" : "today"
}
更新:
"CreatedOn": ISODate("2017-03-31T20:27:12.914+05:00")
你的比较不起作用的原因是
var start = new DateTime(2017, 03, 31);
var end = new DateTime(2017, 03, 31);
这将作为$gte
比ISODate("2017-03-31T00:00:00.000+05:00")
更大,并且比ISODate("2017-03-31T00:00:00.000+05:00")
更小的方式发送到服务器,但它没有找到上述条目。
查询今天日期的正确方法如下:
var start = new DateTime(2017, 03, 31);
var end = new DateTime(2017, 04, 01);
并更新您的过滤器以
var filter = filterBuilder.Gte(x => x.CreatedOn, start) &
filterBuilder.Lt(x => x.CreatedOn, end);
现在您的范围查询已经作为$gte
和ISODate("2017-03-31T00:00:00.000+05:00")
之间的条件以及$lt
和ISODate("2017-04-01T00:00:00.000+05:00")
之间的条件发送到服务器,这样您应该能够找到今天的所有匹配项。
更新2
将您的数据库更改为将日期时间的时间部分设置为00:00:00。这将从数据库中删除时间部分,并且您的旧范围查询将在所有情况下正常工作。
更改您的保存方法以使用
var today = DateTime.Today; //2017-03-31 00:00:00.000
您可以返回到旧的过滤器定义。
类似以下操作
var start = new DateTime(2017, 03, 31);
var end = new DateTime(2017, 03, 31);
并更新您的筛选器为
var filter = filterBuilder.Gte(x => x.CreatedOn, start) &
filterBuilder.Lte(x => x.CreatedOn, end);
现在你的范围查询已发送到服务器作为$gte
和ISODate("2017-03-31T00:00:00.000+05:00")
的比较,以及$lte
和ISODate("2017-03-31T00:00:00.000+05:00")
的比较,你应该能够找到所有今天的匹配项。
更新3 - 使用BsonDocument
进行日期比较。
这里的想法是添加时区偏移量+5:00
到服务器的UTC日期,并将计算出的日期时间转换为字符串yyyy-MM-dd
格式,使用$dateToSting
运算符后跟输入字符串日期的相同格式进行比较。
这将在你的时区中工作,但不适用于观察夏令时的时区。
Mongo版本3.4
您可以使用$addFields
阶段添加新字段CreatedOnDate
,同时保留所有现有属性和最后的$project
,在比较后从最终响应中删除CreatedOnDate
。
Shell查询:
{
"$addFields": {
"CreatedOnDate": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": {
"$add": ["$CreatedOn", 18000000]
}
}
}
}
}, {
"$match": {
"CreatedOnDate": {
"$gte": "2017-03-31",
"$lte": "2017-03-31"
}
}
}, {
"$project": {
"CreatedOnDate": 0
}
}
C#代码:
var start = new DateTime(2017, 03, 31);
var end = new DateTime(2017, 03, 31);
var addFields = BsonDocument.Parse("{$addFields: { CreatedOnDate: { $dateToString: { format: '%Y-%m-%d', date: {$add: ['$CreatedOn', 18000000] }} }} }");
var match = new BsonDocument("CreatedOnDate", new BsonDocument("$gte", start.ToString("yyyy-MM-dd")).Add("$lte", end.ToString("yyyy-MM-dd")));
var project = new BsonDocument
{
{ "CreatedOnDate", 0 }
};
var pipeline = collection.Aggregate().AppendStage<BsonDocument>(addFields)
.Match(match)
.Project(project);
var list = pipeline.ToList();
List<Student> searchResult = list.Select(doc => BsonSerializer.Deserialize<Student>(doc)).ToList();
Mongo版本 = 3.2
与上面相同,但此流水线使用$project
,因此您必须添加要在最终响应中保留的所有字段。
Shell查询:
{
"$project": {
"CreatedOn": 1,
"Description": 1,
"CreatedOnDate": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": {
"$add": ["$CreatedOn", 18000000]
}
}
}
}
}, {
"$match": {
"CreatedOnDate": {
"$gte": "2017-03-31",
"$lte": "2017-03-31"
}
}
}, {
"$project": {
"CreatedOn": 1,
"Description": 1
}
}
C# 代码:
var start = new DateTime(2017, 03, 31);
var end = new DateTime(2017, 03, 31);
var project1 = new BsonDocument
{
{ "CreatedOn", 1 },
{ "Description", 1 },
{ "CreatedOnDate", new BsonDocument("$dateToString", new BsonDocument("format", "%Y-%m-%d")
.Add("date", new BsonDocument("$add", new BsonArray(new object[] { "$CreatedOn", 5 * 60 * 60 * 1000 }))))
}
};
var match = new BsonDocument("CreatedOnDate", new BsonDocument("$gte", start.ToString("yyyy-MM-dd")).Add("$lte", end.ToString("yyyy-MM-dd")));
var project2 = new BsonDocument
{
{ "CreatedOn", 1 },
{ "Description", 1 }
};
var pipeline = collection.Aggregate()
.Project(project1)
.Match(match)
.Project(project2);
var list = pipeline.ToList();
List<Student> searchResult = list.Select(doc => BsonSerializer.Deserialize<Student>(doc)).ToList();
更新 4 - 只比较日期,并考虑夏令时的影响。
Mongo 版本 = 3.6
除了 $dateToString
将使用时区而不是固定偏移量之外,其他都保持不变,这应该考虑到夏令时的变化。
Shell 更新:
{
"$addFields": {
"CreatedOnDate": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": "$CreatedOn",
"timezone": "America/New_York"
}
}
}
}
C# 更新:
var addFields = BsonDocument.Parse("{$addFields: { CreatedOnDate: { $dateToString: { format: '%Y-%m-%d', date: "$CreatedOn", "timezone": "America/New_York"} }} }");