Skip to content

Commit 48f899f

Browse files
committed
CSHARP-2014: added support for $dateFromParts and $dateFromString.
1 parent c1f4e87 commit 48f899f

File tree

4 files changed

+121
-5
lines changed

4 files changed

+121
-5
lines changed

src/MongoDB.Driver/Linq/Processors/SerializerBuilder.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
using System.Linq.Expressions;
2020
using System.Reflection;
2121
using MongoDB.Bson.Serialization;
22+
using MongoDB.Bson.Serialization.Serializers;
2223
using MongoDB.Driver.Linq.Expressions;
2324

2425
namespace MongoDB.Driver.Linq.Processors
@@ -89,8 +90,6 @@ public IBsonSerializer Build(Expression node)
8990
return serializer;
9091
}
9192

92-
93-
9493
private IBsonSerializer BuildMemberInit(MemberInitExpression node)
9594
{
9695
var mapping = ProjectionMapper.Map(node);
@@ -99,6 +98,11 @@ private IBsonSerializer BuildMemberInit(MemberInitExpression node)
9998

10099
private IBsonSerializer BuildNew(NewExpression node)
101100
{
101+
if (node.Type == typeof(DateTime))
102+
{
103+
return DateTimeSerializer.UtcInstance;
104+
}
105+
102106
var mapping = ProjectionMapper.Map(node);
103107
return BuildProjectedSerializer(mapping);
104108
}

src/MongoDB.Driver/Linq/Translators/AggregateLanguageTranslator.cs

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,12 @@ private BsonValue TranslateMethodCall(MethodCallExpression node)
372372
{
373373
return result;
374374
}
375+
376+
if (node.Method.DeclaringType == typeof(DateTime)
377+
&& TryTranslateStaticDateTimeMethodCall(node, out result))
378+
{
379+
return result;
380+
}
375381
}
376382
else
377383
{
@@ -423,6 +429,10 @@ private BsonValue TranslateMemberInit(MemberInitExpression node)
423429

424430
private BsonValue TranslateNew(NewExpression node)
425431
{
432+
if (node.Type == typeof(DateTime))
433+
{
434+
return TranslateNewDateTime(node);
435+
}
426436
var mapping = ProjectionMapper.Map(node);
427437
return TranslateMapping(mapping);
428438
}
@@ -437,6 +447,54 @@ private BsonValue TranslateNewArrayInit(NewArrayExpression node)
437447
return bsonArray;
438448
}
439449

450+
private BsonValue TranslateNewDateTime(NewExpression node)
451+
{
452+
BsonValue year = null;
453+
BsonValue month = null;
454+
BsonValue day = null;
455+
BsonValue hour = null;
456+
BsonValue minute = null;
457+
BsonValue second = null;
458+
BsonValue millisecond = null;
459+
460+
switch (node.Arguments.Count)
461+
{
462+
case 3:
463+
year = TranslateValue(node.Arguments[0]);
464+
month = TranslateValue(node.Arguments[1]);
465+
day = TranslateValue(node.Arguments[2]);
466+
break;
467+
case 6:
468+
hour = TranslateValue(node.Arguments[3]);
469+
minute = TranslateValue(node.Arguments[4]);
470+
second = TranslateValue(node.Arguments[5]);
471+
goto case 3;
472+
case 7:
473+
if (node.Arguments[6].Type == typeof(int))
474+
{
475+
millisecond = TranslateValue(node.Arguments[6]);
476+
goto case 6;
477+
}
478+
break;
479+
}
480+
481+
if (year == null)
482+
{
483+
throw new NotSupportedException($"The DateTime constructor {node} is not supported.");
484+
}
485+
486+
return new BsonDocument("$dateFromParts", new BsonDocument
487+
{
488+
{ "year", year, year != null },
489+
{ "month", month, month != null },
490+
{ "day", day, day != null },
491+
{ "hour", hour, hour != null },
492+
{ "minute", minute, minute != null },
493+
{ "second", second, second != null },
494+
{ "millisecond", millisecond, millisecond != null }
495+
});
496+
}
497+
440498
private BsonValue TranslateMapping(ProjectionMapping mapping)
441499
{
442500
BsonDocument doc = new BsonDocument();
@@ -763,8 +821,9 @@ private bool TryTranslateDateTimeCall(MethodCallExpression node, out BsonValue r
763821
{ "format", format },
764822
{ "date", field }
765823
});
824+
return true;
766825
}
767-
return true;
826+
break;
768827
}
769828

770829
return false;
@@ -916,6 +975,26 @@ private bool TryTranslateMinResultOperator(PipelineExpression node, out BsonValu
916975
return false;
917976
}
918977

978+
private bool TryTranslateStaticDateTimeMethodCall(MethodCallExpression node, out BsonValue result)
979+
{
980+
result = null;
981+
switch (node.Method.Name)
982+
{
983+
case "Parse":
984+
if (node.Arguments.Count == 1)
985+
{
986+
result = new BsonDocument("$dateFromString", new BsonDocument
987+
{
988+
{ "dateString", TranslateValue(node.Arguments[0]) }
989+
});
990+
return true;
991+
}
992+
break;
993+
}
994+
995+
return false;
996+
}
997+
919998
private bool TryTranslateStaticEnumerableMethodCall(MethodCallExpression node, out BsonValue result)
920999
{
9211000
result = null;

tests/MongoDB.Driver.Tests/Linq/IntegrationTestBase.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2010-2016 MongoDB Inc.
1+
/* Copyright 2010-2017 MongoDB Inc.
22
*
33
* Licensed under the Apache License, Version 2.0 (the "License");
44
* you may not use this file except in compliance with the License.
@@ -103,7 +103,8 @@ private void InsertFirst()
103103
Q = Q.One,
104104
R = new DateTime(2013, 1, 2, 3, 4, 5, 6, DateTimeKind.Utc),
105105
T = new Dictionary<string, int> { { "one", 1 }, { "two", 2 } },
106-
U = 1.23456571661743267789m
106+
U = 1.23456571661743267789m,
107+
V = "2017-02-08T12:10:40.787"
107108
};
108109
__collection.InsertOne(root);
109110
}
@@ -219,6 +220,8 @@ public class Root : IRoot
219220

220221
[BsonRepresentation(Bson.BsonType.Double, AllowTruncation = true)]
221222
public decimal U { get; set; }
223+
224+
public string V { get; set; }
222225
}
223226

224227
public class RootDescended : Root

tests/MongoDB.Driver.Tests/Linq/Translators/AggregateProjectTranslatorTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,24 @@ public void Should_translate_multiply_flattened()
815815
result.Value.Result.Should().Be(2420);
816816
}
817817

818+
[SkippableFact]
819+
public void Should_translate_new_DateTime()
820+
{
821+
RequireServer.Check().VersionGreaterThanOrEqualTo("3.6.0-rc0");
822+
823+
var result = Project(x => new { Result = new DateTime(x.Id, 11, 12) });
824+
result.Projection.Should().Be("{ Result: { \"$dateFromParts\": { \"year\": \"$_id\", \"month\": 11, \"day\": 12 } }, _id: 0 }");
825+
result.Value.Result.Should().Be(new DateTime(10, 11, 12));
826+
827+
result = Project(x => new { Result = new DateTime(x.Id, 11, 12, 1, 2, 3) });
828+
result.Projection.Should().Be("{ Result: { \"$dateFromParts\": { \"year\": \"$_id\", \"month\": 11, \"day\": 12, \"hour\": 1, \"minute\": 2, \"second\": 3 } }, _id: 0 }");
829+
result.Value.Result.Should().Be(new DateTime(10, 11, 12, 1, 2, 3));
830+
831+
result = Project(x => new { Result = new DateTime(x.Id, 11, 12, 1, 2, 3, 4) });
832+
result.Projection.Should().Be("{ Result: { \"$dateFromParts\": { \"year\": \"$_id\", \"month\": 11, \"day\": 12, \"hour\": 1, \"minute\": 2, \"second\": 3, \"millisecond\": 4 } }, _id: 0 }");
833+
result.Value.Result.Should().Be(new DateTime(10, 11, 12, 1, 2, 3, 4));
834+
}
835+
818836
[Fact]
819837
public void Should_translate_not()
820838
{
@@ -865,6 +883,18 @@ public void Should_translate_or_flattened()
865883
result.Value.Result.Should().BeFalse();
866884
}
867885

886+
[SkippableFact]
887+
public void Should_translate_DateTime_parse()
888+
{
889+
RequireServer.Check().VersionGreaterThanOrEqualTo("3.6.0-rc0");
890+
891+
var result = Project(x => new { Result = DateTime.Parse(x.V) });
892+
893+
result.Projection.Should().Be("{ Result: { \"$dateFromString\": { \"dateString\": \"$V\" } }, _id: 0 }");
894+
895+
result.Value.Result.Should().Be(new DateTime(2017, 2, 8, 12, 10, 40, 787, DateTimeKind.Utc));
896+
}
897+
868898
[SkippableFact]
869899
public void Should_translate_pow()
870900
{

0 commit comments

Comments
 (0)