Skip to content

Commit 9aa140d

Browse files
committed
CSHARP-3136: Add full support for $dateToString in LINQ3.
1 parent 94a9346 commit 9aa140d

File tree

6 files changed

+406
-30
lines changed

6 files changed

+406
-30
lines changed

src/MongoDB.Driver/Linq/DateTimeExtensions.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,18 @@ public static DateTime Subtract(this DateTime @this, long value, DateTimeUnit un
279279
throw new InvalidOperationException("This DateTime.Subtract method is only intended to be used in LINQ queries.");
280280
}
281281

282+
/// <summary>
283+
/// Converts a DateTime value to a string.
284+
/// </summary>
285+
/// <param name="this">The DateTime value.</param>
286+
/// <param name="format">The format string (optional, can be null).</param>
287+
/// <param name="timezone">The timezone to use in the returned string (optional, can be null).</param>
288+
/// <returns>The DateTime value converted to a string.</returns>
289+
public static string ToString(this DateTime @this, string format, string timezone)
290+
{
291+
throw new InvalidOperationException("This DateTime.ToString method is only intended to be used in LINQ queries.");
292+
}
293+
282294
/// <summary>
283295
/// Truncates a DateTime value to the specified unit.
284296
/// </summary>

src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/DateTimeMethod.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ internal static class DateTimeMethod
5353
private static readonly MethodInfo __subtractWithTimeSpanAndTimezone;
5454
private static readonly MethodInfo __subtractWithUnit;
5555
private static readonly MethodInfo __subtractWithUnitAndTimezone;
56+
private static readonly MethodInfo __toStringWithFormat;
57+
private static readonly MethodInfo __toStringWithFormatAndTimezone;
5658
private static readonly MethodInfo __truncate;
5759
private static readonly MethodInfo __truncateWithBinSize;
5860
private static readonly MethodInfo __truncateWithBinSizeAndTimezone;
@@ -92,6 +94,8 @@ static DateTimeMethod()
9294
__subtractWithTimeSpanAndTimezone = ReflectionInfo.Method((DateTime @this, TimeSpan value, string timezone) => @this.Subtract(value, timezone));
9395
__subtractWithUnit = ReflectionInfo.Method((DateTime @this, long value, DateTimeUnit unit) => @this.Subtract(value, unit));
9496
__subtractWithUnitAndTimezone = ReflectionInfo.Method((DateTime @this, long value, DateTimeUnit unit, string timezone) => @this.Subtract(value, unit, timezone));
97+
__toStringWithFormat = ReflectionInfo.Method((DateTime @this, string format) => @this.ToString(format));
98+
__toStringWithFormatAndTimezone = ReflectionInfo.Method((DateTime @this, string format, string timezone) => @this.ToString(format, timezone));
9599
__truncate = ReflectionInfo.Method((DateTime @this, DateTimeUnit unit) => @this.Truncate(unit));
96100
__truncateWithBinSize = ReflectionInfo.Method((DateTime @this, DateTimeUnit unit, long binSize) => @this.Truncate(unit, binSize));
97101
__truncateWithBinSizeAndTimezone = ReflectionInfo.Method((DateTime @this, DateTimeUnit unit, long binSize, string timezone) => @this.Truncate(unit, binSize, timezone));
@@ -130,6 +134,8 @@ static DateTimeMethod()
130134
public static MethodInfo SubtractWithTimeSpanAndTimezone => __subtractWithTimeSpanAndTimezone;
131135
public static MethodInfo SubtractWithUnit => __subtractWithUnit;
132136
public static MethodInfo SubtractWithUnitAndTimezone => __subtractWithUnitAndTimezone;
137+
public static MethodInfo ToStringWithFormat => __toStringWithFormat;
138+
public static MethodInfo ToStringWithFormatAndTimezone => __toStringWithFormatAndTimezone;
133139
public static MethodInfo Truncate => __truncate;
134140
public static MethodInfo TruncateWithBinSize => __truncateWithBinSize;
135141
public static MethodInfo TruncateWithBinSizeAndTimezone => __truncateWithBinSizeAndTimezone;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/* Copyright 2010-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
using System.Reflection;
18+
19+
namespace MongoDB.Driver.Linq.Linq3Implementation.Reflection
20+
{
21+
internal static class NullableDateTimeMethod
22+
{
23+
// private static fields
24+
private static readonly MethodInfo __toStringWithFormatAndTimezoneAndOnNull;
25+
26+
// static constructor
27+
static NullableDateTimeMethod()
28+
{
29+
__toStringWithFormatAndTimezoneAndOnNull = ReflectionInfo.Method((DateTime? @this, string format, string timezone, string onNull) => @this.ToString(format, timezone, onNull));
30+
}
31+
32+
// public properties
33+
public static MethodInfo ToStringWithFormatAndTimezoneAndOnNull => __toStringWithFormatAndTimezoneAndOnNull;
34+
}
35+
}

src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ToStringMethodToAggregationExpressionTranslator.cs

Lines changed: 76 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,64 +13,110 @@
1313
* limitations under the License.
1414
*/
1515

16-
using System;
16+
using System.Linq;
1717
using System.Linq.Expressions;
18+
using System.Reflection;
1819
using MongoDB.Bson.Serialization.Serializers;
1920
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions;
21+
using MongoDB.Driver.Linq.Linq3Implementation.Misc;
22+
using MongoDB.Driver.Linq.Linq3Implementation.Reflection;
2023

2124
namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MethodTranslators
2225
{
2326
internal static class ToStringMethodToAggregationExpressionTranslator
2427
{
28+
private static readonly MethodInfo[] __dateTimeToStringMethods = new[]
29+
{
30+
DateTimeMethod.ToStringWithFormat,
31+
DateTimeMethod.ToStringWithFormatAndTimezone,
32+
NullableDateTimeMethod.ToStringWithFormatAndTimezoneAndOnNull,
33+
};
34+
35+
private static readonly MethodInfo[] __dateTimeToStringMethodsWithTimezone = new[]
36+
{
37+
DateTimeMethod.ToStringWithFormatAndTimezone,
38+
NullableDateTimeMethod.ToStringWithFormatAndTimezoneAndOnNull,
39+
};
40+
41+
private static readonly MethodInfo[] __dateTimeToStringMethodsWithOnNull = new[]
42+
{
43+
NullableDateTimeMethod.ToStringWithFormatAndTimezoneAndOnNull,
44+
};
45+
2546
public static AggregationExpression Translate(TranslationContext context, MethodCallExpression expression)
2647
{
27-
if (TryTranslateInstanceToStringWithNoArguments(context, expression, out var translation) ||
28-
TryTranslateDateTimeToStringWithFormat(context, expression, out translation))
48+
var method = expression.Method;
49+
var arguments = expression.Arguments.ToArray();
50+
51+
if (IsInstanceToStringMethodWithNoArguments(method))
2952
{
30-
return translation;
53+
return TranslateInstanceToStringMethodWithNoArguments(context, expression);
54+
}
55+
56+
if (method.IsOneOf(__dateTimeToStringMethods))
57+
{
58+
return TranslateDateTimeToStringMethod(context, expression, method, arguments);
3159
}
3260

3361
throw new ExpressionNotSupportedException(expression);
3462
}
3563

36-
private static bool TryTranslateDateTimeToStringWithFormat(TranslationContext context, MethodCallExpression expression, out AggregationExpression translation)
64+
private static bool IsInstanceToStringMethodWithNoArguments(MethodInfo method)
3765
{
38-
var method = expression.Method;
39-
var arguments = expression.Arguments;
66+
return
67+
!method.IsStatic &&
68+
method.Name == "ToString" &&
69+
method.ReturnType == typeof(string) &&
70+
method.GetParameters().Length == 0;
71+
}
4072

41-
if (method.DeclaringType != typeof(DateTime) || method.IsStatic || method.ReturnType != typeof(string) || method.Name != "ToString" || arguments.Count != 1 || arguments[0].Type != typeof(string))
42-
{
43-
translation = null;
44-
return false;
45-
}
73+
private static AggregationExpression TranslateDateTimeToStringMethod(TranslationContext context, MethodCallExpression expression, MethodInfo method, Expression[] arguments)
74+
{
4675

47-
var dateTimeExpression = expression.Object;
76+
var dateTimeExpression = method.IsStatic ? arguments[0] : expression.Object;
4877
var dateTimeTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, dateTimeExpression);
49-
var formatExpression = arguments[0];
50-
var formatTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, formatExpression);
51-
var ast = AstExpression.DateToString(dateTimeTranslation.Ast, formatTranslation.Ast);
52-
translation = new AggregationExpression(expression, ast, new StringSerializer());
53-
return true;
54-
}
78+
var dateAst = dateTimeTranslation.Ast;
5579

56-
private static bool TryTranslateInstanceToStringWithNoArguments(TranslationContext context, MethodCallExpression expression, out AggregationExpression translation)
57-
{
58-
var method = expression.Method;
59-
var arguments = expression.Arguments;
60-
translation = null;
80+
AstExpression formatAst = null;
81+
var formatExpression = method.IsStatic ? arguments[1] : arguments[0];
82+
if (!(formatExpression is ConstantExpression constantExprssion) || constantExprssion.Value != null)
83+
{
84+
var formatTranslataion = ExpressionToAggregationExpressionTranslator.Translate(context, formatExpression);
85+
formatAst = formatTranslataion.Ast;
86+
}
6187

62-
if (method.IsStatic || method.ReturnType != typeof(string) || method.Name != "ToString" || arguments.Count != 0)
88+
AstExpression timezoneAst = null;
89+
if (method.IsOneOf(__dateTimeToStringMethodsWithTimezone))
6390
{
64-
return false;
91+
var timezoneExpression = arguments[2];
92+
if (!(timezoneExpression is ConstantExpression constantExpression) || constantExpression.Value != null)
93+
{
94+
var timezoneTranslataion = ExpressionToAggregationExpressionTranslator.Translate(context, timezoneExpression);
95+
timezoneAst = timezoneTranslataion.Ast;
96+
}
6597
}
6698

67-
var objectExpression = expression.Object;
99+
AstExpression onNullAst = null;
100+
if (method.IsOneOf(__dateTimeToStringMethodsWithOnNull))
101+
{
102+
var onNullExpression = arguments[3];
103+
var onNullTranslataion = ExpressionToAggregationExpressionTranslator.Translate(context, onNullExpression);
104+
if (!(onNullExpression is ConstantExpression constantExpression) || constantExpression.Value != null)
105+
{
106+
onNullAst = onNullTranslataion.Ast;
107+
}
108+
}
109+
110+
var ast = AstExpression.DateToString(dateAst, formatAst, timezoneAst, onNullAst);
111+
return new AggregationExpression(expression, ast, StringSerializer.Instance);
112+
}
68113

114+
private static AggregationExpression TranslateInstanceToStringMethodWithNoArguments(TranslationContext context, MethodCallExpression expression)
115+
{
116+
var objectExpression = expression.Object;
69117
var objectTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, objectExpression);
70118
var ast = AstExpression.ToString(objectTranslation.Ast);
71-
72-
translation = new AggregationExpression(expression, ast, new StringSerializer());
73-
return true;
119+
return new AggregationExpression(expression, ast, StringSerializer.Instance);
74120
}
75121
}
76122
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+

2+
/* Copyright 2016-present MongoDB Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
using System;
18+
19+
namespace MongoDB.Driver.Linq
20+
{
21+
/// <summary>
22+
/// This static class holds methods that can be used to express MongoDB specific operations in LINQ queries.
23+
/// </summary>
24+
public static class NullableDateTimeExtensions
25+
{
26+
/// <summary>
27+
/// Converts a NullableDateTime value to a string.
28+
/// </summary>
29+
/// <param name="this">The NullableDateTime value.</param>
30+
/// <param name="format">The format string (optional, can be null).</param>
31+
/// <param name="timezone">The timezone to use in the returned string (optional, can be null).</param>
32+
/// <param name="onNull">The string to return if the NullableDateTime value is null.</param>
33+
/// <returns>The NullableDateTime value converted to a string.</returns>
34+
public static string ToString(this DateTime? @this, string format, string timezone, string onNull)
35+
{
36+
throw new InvalidOperationException("This DateTime.ToString method is only intended to be used in LINQ queries.");
37+
}
38+
}
39+
}

0 commit comments

Comments
 (0)