Skip to content

Commit beaf6eb

Browse files
authored
Fix issue where using DateTime.UtcNow and DateTime.Now will lose precision when used inside of a query (#1988)
* Fix issue where using `DateTime.UtcNow` and `DateTime.Now` will lose precision when used inside of a query Resolves issue: #1987 * Add is DateTime(6) supported check for `DateTime.Now` and `DateTime.UtcNow` translators - Fix failing tests
1 parent 6ca7cba commit beaf6eb

6 files changed

Lines changed: 39 additions & 28 deletions

File tree

src/EFCore.MySql/Query/Internal/MySqlDateTimeMemberTranslator.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using Microsoft.EntityFrameworkCore.Diagnostics;
99
using Microsoft.EntityFrameworkCore.Query;
1010
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
11+
using Pomelo.EntityFrameworkCore.MySql.Infrastructure.Internal;
1112
using Pomelo.EntityFrameworkCore.MySql.Utilities;
1213

1314
namespace Pomelo.EntityFrameworkCore.MySql.Query.Internal
@@ -26,10 +27,12 @@ public class MySqlDateTimeMemberTranslator : IMemberTranslator
2627
{ nameof(DateTime.Millisecond), ("microsecond", 1000) },
2728
};
2829
private readonly MySqlSqlExpressionFactory _sqlExpressionFactory;
30+
private readonly IMySqlOptions _mySqlOptions;
2931

30-
public MySqlDateTimeMemberTranslator(ISqlExpressionFactory sqlExpressionFactory)
32+
public MySqlDateTimeMemberTranslator(ISqlExpressionFactory sqlExpressionFactory, IMySqlOptions mySqlOptions)
3133
{
3234
_sqlExpressionFactory = (MySqlSqlExpressionFactory)sqlExpressionFactory;
35+
_mySqlOptions = mySqlOptions;
3336
}
3437

3538
public virtual SqlExpression Translate(
@@ -101,13 +104,17 @@ public virtual SqlExpression Translate(
101104
declaringType == typeof(DateTimeOffset)
102105
? "UTC_TIMESTAMP"
103106
: "CURRENT_TIMESTAMP",
104-
Array.Empty<SqlExpression>(),
107+
_mySqlOptions.ServerVersion.Supports.DateTime6 ?
108+
new [] { _sqlExpressionFactory.Constant(6)} :
109+
Array.Empty<SqlExpression>(),
105110
returnType);
106111

107112
case nameof(DateTime.UtcNow):
108113
return _sqlExpressionFactory.NonNullableFunction(
109114
"UTC_TIMESTAMP",
110-
Array.Empty<SqlExpression>(),
115+
_mySqlOptions.ServerVersion.Supports.DateTime6 ?
116+
new [] { _sqlExpressionFactory.Constant(6)} :
117+
ArraySegment<SqlExpression>.Empty,
111118
returnType);
112119

113120
case nameof(DateTime.Today):

src/EFCore.MySql/Query/Internal/MySqlMemberTranslatorProvider.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,20 @@
33

44
using JetBrains.Annotations;
55
using Microsoft.EntityFrameworkCore.Query;
6+
using Pomelo.EntityFrameworkCore.MySql.Infrastructure.Internal;
67

78
namespace Pomelo.EntityFrameworkCore.MySql.Query.Internal
89
{
910
public class MySqlMemberTranslatorProvider : RelationalMemberTranslatorProvider
1011
{
11-
public MySqlMemberTranslatorProvider([NotNull] RelationalMemberTranslatorProviderDependencies dependencies)
12+
public MySqlMemberTranslatorProvider([NotNull] RelationalMemberTranslatorProviderDependencies dependencies, IMySqlOptions mySqlOptions)
1213
: base(dependencies)
1314
{
1415
var sqlExpressionFactory = (MySqlSqlExpressionFactory)dependencies.SqlExpressionFactory;
1516

1617
AddTranslators(
1718
new IMemberTranslator[] {
18-
new MySqlDateTimeMemberTranslator(sqlExpressionFactory),
19+
new MySqlDateTimeMemberTranslator(sqlExpressionFactory, mySqlOptions),
1920
new MySqlStringMemberTranslator(sqlExpressionFactory),
2021
new MySqlTimeSpanMemberTranslator(sqlExpressionFactory),
2122
});

test/EFCore.MySql.FunctionalTests/Query/NorthwindDbFunctionsQueryMySqlTest.MySql.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public virtual void DateDiff_Year()
2020
AssertSql(
2121
@"SELECT COUNT(*)
2222
FROM `Orders` AS `o`
23-
WHERE TIMESTAMPDIFF(YEAR, `o`.`OrderDate`, CURRENT_TIMESTAMP()) = 0");
23+
WHERE TIMESTAMPDIFF(YEAR, `o`.`OrderDate`, CURRENT_TIMESTAMP(6)) = 0");
2424
}
2525
}
2626

@@ -37,7 +37,7 @@ public virtual void DateDiff_Quarter()
3737
AssertSql(
3838
@"SELECT COUNT(*)
3939
FROM `Orders` AS `o`
40-
WHERE TIMESTAMPDIFF(QUARTER, `o`.`OrderDate`, CURRENT_TIMESTAMP()) = 0");
40+
WHERE TIMESTAMPDIFF(QUARTER, `o`.`OrderDate`, CURRENT_TIMESTAMP(6)) = 0");
4141
}
4242
}
4343

@@ -54,7 +54,7 @@ public virtual void DateDiff_Week()
5454
AssertSql(
5555
@"SELECT COUNT(*)
5656
FROM `Orders` AS `o`
57-
WHERE TIMESTAMPDIFF(WEEK, `o`.`OrderDate`, CURRENT_TIMESTAMP()) = 0");
57+
WHERE TIMESTAMPDIFF(WEEK, `o`.`OrderDate`, CURRENT_TIMESTAMP(6)) = 0");
5858
}
5959
}
6060

@@ -71,7 +71,7 @@ public virtual void DateDiff_Month()
7171
AssertSql(
7272
@"SELECT COUNT(*)
7373
FROM `Orders` AS `o`
74-
WHERE TIMESTAMPDIFF(MONTH, `o`.`OrderDate`, CURRENT_TIMESTAMP()) = 0");
74+
WHERE TIMESTAMPDIFF(MONTH, `o`.`OrderDate`, CURRENT_TIMESTAMP(6)) = 0");
7575
}
7676
}
7777

@@ -88,7 +88,7 @@ public virtual void DateDiff_Day()
8888
AssertSql(
8989
@"SELECT COUNT(*)
9090
FROM `Orders` AS `o`
91-
WHERE TIMESTAMPDIFF(DAY, `o`.`OrderDate`, CURRENT_TIMESTAMP()) = 0");
91+
WHERE TIMESTAMPDIFF(DAY, `o`.`OrderDate`, CURRENT_TIMESTAMP(6)) = 0");
9292
}
9393
}
9494

@@ -105,7 +105,7 @@ public virtual void DateDiff_Hour()
105105
AssertSql(
106106
@"SELECT COUNT(*)
107107
FROM `Orders` AS `o`
108-
WHERE TIMESTAMPDIFF(HOUR, `o`.`OrderDate`, CURRENT_TIMESTAMP()) = 0");
108+
WHERE TIMESTAMPDIFF(HOUR, `o`.`OrderDate`, CURRENT_TIMESTAMP(6)) = 0");
109109
}
110110
}
111111

@@ -122,7 +122,7 @@ public virtual void DateDiff_Minute()
122122
AssertSql(
123123
@"SELECT COUNT(*)
124124
FROM `Orders` AS `o`
125-
WHERE TIMESTAMPDIFF(MINUTE, `o`.`OrderDate`, CURRENT_TIMESTAMP()) = 0");
125+
WHERE TIMESTAMPDIFF(MINUTE, `o`.`OrderDate`, CURRENT_TIMESTAMP(6)) = 0");
126126
}
127127
}
128128

@@ -139,7 +139,7 @@ public virtual void DateDiff_Second()
139139
AssertSql(
140140
@"SELECT COUNT(*)
141141
FROM `Orders` AS `o`
142-
WHERE TIMESTAMPDIFF(SECOND, `o`.`OrderDate`, CURRENT_TIMESTAMP()) = 0");
142+
WHERE TIMESTAMPDIFF(SECOND, `o`.`OrderDate`, CURRENT_TIMESTAMP(6)) = 0");
143143
}
144144
}
145145

@@ -156,7 +156,7 @@ public virtual void DateDiff_Millisecond()
156156
AssertSql(
157157
@"SELECT COUNT(*)
158158
FROM `Orders` AS `o`
159-
WHERE (TIMESTAMPDIFF(MICROSECOND, CURRENT_TIMESTAMP(), DATE_ADD(CURRENT_TIMESTAMP(), INTERVAL CAST(1.0 AS signed) second))) DIV (1000) = 0");
159+
WHERE (TIMESTAMPDIFF(MICROSECOND, CURRENT_TIMESTAMP(6), DATE_ADD(CURRENT_TIMESTAMP(6), INTERVAL CAST(1.0 AS signed) second))) DIV (1000) = 0");
160160
}
161161
}
162162

@@ -173,7 +173,7 @@ public virtual void DateDiff_Microsecond()
173173
AssertSql(
174174
@"SELECT COUNT(*)
175175
FROM `Orders` AS `o`
176-
WHERE TIMESTAMPDIFF(MICROSECOND, CURRENT_TIMESTAMP(), DATE_ADD(CURRENT_TIMESTAMP(), INTERVAL CAST(1.0 AS signed) second)) = 0");
176+
WHERE TIMESTAMPDIFF(MICROSECOND, CURRENT_TIMESTAMP(6), DATE_ADD(CURRENT_TIMESTAMP(6), INTERVAL CAST(1.0 AS signed) second)) = 0");
177177
}
178178
}
179179

@@ -190,7 +190,7 @@ public virtual void DateDiff_Tick()
190190
AssertSql(
191191
@"SELECT COUNT(*)
192192
FROM `Orders` AS `o`
193-
WHERE (TIMESTAMPDIFF(MICROSECOND, CURRENT_TIMESTAMP(), DATE_ADD(CURRENT_TIMESTAMP(), INTERVAL CAST(1.0 AS signed) second)) * 10) = 0");
193+
WHERE (TIMESTAMPDIFF(MICROSECOND, CURRENT_TIMESTAMP(6), DATE_ADD(CURRENT_TIMESTAMP(6), INTERVAL CAST(1.0 AS signed) second)) * 10) = 0");
194194
}
195195
}
196196

@@ -207,7 +207,7 @@ public virtual void DateDiff_Nanosecond()
207207
AssertSql(
208208
@"SELECT COUNT(*)
209209
FROM `Orders` AS `o`
210-
WHERE (TIMESTAMPDIFF(MICROSECOND, CURRENT_TIMESTAMP(), DATE_ADD(CURRENT_TIMESTAMP(), INTERVAL CAST(1.0 AS signed) second)) * 1000) = 0");
210+
WHERE (TIMESTAMPDIFF(MICROSECOND, CURRENT_TIMESTAMP(6), DATE_ADD(CURRENT_TIMESTAMP(6), INTERVAL CAST(1.0 AS signed) second)) * 1000) = 0");
211211
}
212212
}
213213

test/EFCore.MySql.FunctionalTests/Query/NorthwindWhereQueryMySqlTest.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public override async Task Where_datetime_now(bool async)
3333
3434
SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
3535
FROM `Customers` AS `c`
36-
WHERE CURRENT_TIMESTAMP() <> @__myDatetime_0");
36+
WHERE CURRENT_TIMESTAMP(6) <> @__myDatetime_0");
3737
}
3838

3939
[ConditionalTheory]
@@ -46,7 +46,7 @@ public override async Task Where_datetime_utcnow(bool async)
4646
4747
SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
4848
FROM `Customers` AS `c`
49-
WHERE UTC_TIMESTAMP() <> @__myDatetime_0");
49+
WHERE UTC_TIMESTAMP(6) <> @__myDatetime_0");
5050
}
5151

5252
[ConditionalTheory]
@@ -57,7 +57,7 @@ public override async Task Where_datetime_today(bool async)
5757
AssertSql(
5858
@"SELECT `e`.`EmployeeID`, `e`.`City`, `e`.`Country`, `e`.`FirstName`, `e`.`ReportsTo`, `e`.`Title`
5959
FROM `Employees` AS `e`
60-
WHERE CONVERT(CURRENT_TIMESTAMP(), date) = CURDATE()");
60+
WHERE CONVERT(CURRENT_TIMESTAMP(6), date) = CURDATE()");
6161
}
6262

6363
[ConditionalTheory]

test/EFCore.MySql.FunctionalTests/Query/TPCGearsOfWarQueryMySqlTest.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3845,7 +3845,7 @@ public override async Task Where_datetimeoffset_now(bool async)
38453845
"""
38463846
SELECT `m`.`Id`, `m`.`CodeName`, `m`.`Date`, `m`.`Difficulty`, `m`.`Duration`, `m`.`Rating`, `m`.`Time`, `m`.`Timeline`
38473847
FROM `Missions` AS `m`
3848-
WHERE `m`.`Timeline` <> UTC_TIMESTAMP()
3848+
WHERE `m`.`Timeline` <> UTC_TIMESTAMP(6)
38493849
""");
38503850
}
38513851

@@ -3857,7 +3857,7 @@ public override async Task Where_datetimeoffset_utcnow(bool async)
38573857
"""
38583858
SELECT `m`.`Id`, `m`.`CodeName`, `m`.`Date`, `m`.`Difficulty`, `m`.`Duration`, `m`.`Rating`, `m`.`Time`, `m`.`Timeline`
38593859
FROM `Missions` AS `m`
3860-
WHERE `m`.`Timeline` <> UTC_TIMESTAMP()
3860+
WHERE `m`.`Timeline` <> UTC_TIMESTAMP(6)
38613861
""");
38623862
}
38633863

@@ -9523,7 +9523,7 @@ public override async Task Select_datetimeoffset_comparison_in_projection(bool a
95239523

95249524
AssertSql(
95259525
"""
9526-
SELECT `m`.`Timeline` > UTC_TIMESTAMP()
9526+
SELECT `m`.`Timeline` > UTC_TIMESTAMP(6)
95279527
FROM `Missions` AS `m`
95289528
""");
95299529
}

test/EFCore.MySql.IntegrationTests/Tests/Models/ExpressionTest.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -153,17 +153,20 @@ public async Task MySqlDateTimeNowTranslator()
153153
await _db.Database.ExecuteSqlRawAsync($"SET @@session.time_zone = '{utcOffsetStr}'");
154154
#pragma warning restore EF1002
155155

156+
var utcNow = DateTime.UtcNow;
157+
var now = DateTime.Now;
156158
var result = await _db.DataTypesSimple.Select(m =>
157159
new {
158160
m.Id,
159-
DateTime.Now,
160-
DateTime.UtcNow
161-
}).FirstOrDefaultAsync(m => m.Id == _simple.Id);
161+
Now = now,
162+
UtcNow = utcNow,
163+
}).FirstOrDefaultAsync(m => m.Id == _simple.Id && m.UtcNow < DateTime.UtcNow && m.Now < DateTime.Now);
162164

163165
_db.Database.CloseConnection();
164166

165-
Assert.InRange(result.Now, DateTime.Now - TimeSpan.FromSeconds(5), DateTime.Now + TimeSpan.FromSeconds(5));
166-
Assert.InRange(result.UtcNow, DateTime.UtcNow - TimeSpan.FromSeconds(5), DateTime.UtcNow + TimeSpan.FromSeconds(5));
167+
Assert.NotNull(result);
168+
Assert.Equal(result.Now, DateTime.Now, precision: TimeSpan.FromSeconds(10));
169+
Assert.Equal(result.UtcNow, DateTime.UtcNow, precision: TimeSpan.FromSeconds(10));
167170
}
168171

169172
[Fact]

0 commit comments

Comments
 (0)