Skip to content

Commit

Permalink
Add DateTimeOffset member translations for .DateTime, `.UtcDateTi…
Browse files Browse the repository at this point in the history
…me` and `.LocalDateTime`. (PomeloFoundation#1864)

(cherry picked from commit d75b62e)
  • Loading branch information
lauxjpn committed Mar 16, 2024
1 parent bcbedf4 commit b650ad7
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 0 deletions.
22 changes: 22 additions & 0 deletions src/EFCore.MySql/Query/Internal/MySqlDateTimeMemberTranslator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Pomelo.EntityFrameworkCore.MySql.Utilities;

namespace Pomelo.EntityFrameworkCore.MySql.Query.Internal
{
Expand Down Expand Up @@ -141,6 +142,27 @@ public virtual SqlExpression Translate(
}
}

if (declaringType == typeof(DateTimeOffset))
{
switch (member.Name)
{
case nameof(DateTimeOffset.DateTime):
case nameof(DateTimeOffset.UtcDateTime):
// We represent `DateTimeOffset` values as UTC datetime values in the database. Therefore, `DateTimeOffset`,
// `DateTimeOffset.DateTime` and `DateTimeOffset.UtcDateTime` are all the same.
return _sqlExpressionFactory.Convert(instance, typeof(DateTime));

case nameof(DateTimeOffset.LocalDateTime):
return _sqlExpressionFactory.NullableFunction(
"CONVERT_TZ",
[instance, _sqlExpressionFactory.Constant("+00:00"), _sqlExpressionFactory.Fragment("@@session.time_zone")],
typeof(DateTime),
null,
false,
Statics.GetTrueValues(3));
}
}

return null;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,5 +196,39 @@ await AssertQuery(
WHERE `w`.`IsAutomatic` = TRUE"), keys); // Breaking change in 5.0 due to bool expression optimization in `SqlNullabilityProcessor`.
// Was "`w`.`IsAutomatic` <> FALSE" before.
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task DateTimeOffset_DateTime(bool async)
{
await AssertQuery(
async,
ss => ss.Set<Mission>().Where(e => e.Timeline == e.Timeline.DateTime),
ss => ss.Set<Mission>().Where(e => true));

AssertSql(
"""
SELECT `m`.`Id`, `m`.`CodeName`, `m`.`Date`, `m`.`Duration`, `m`.`Rating`, `m`.`Time`, `m`.`Timeline`
FROM `Missions` AS `m`
WHERE `m`.`Timeline` = `m`.`Timeline`
""");
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task DateTimeOffset_UtcDateTime(bool async)
{
await AssertQuery(
async,
ss => ss.Set<Mission>().Where(e => e.Timeline == e.Timeline.UtcDateTime),
ss => ss.Set<Mission>().Where(e => true));

AssertSql(
"""
SELECT `m`.`Id`, `m`.`CodeName`, `m`.`Date`, `m`.`Duration`, `m`.`Rating`, `m`.`Time`, `m`.`Timeline`
FROM `Missions` AS `m`
WHERE `m`.`Timeline` = `m`.`Timeline`
""");
}
}
}
25 changes: 25 additions & 0 deletions test/EFCore.MySql.Tests/Query/MySqlTimeZoneTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,30 @@ LIMIT 2
Fixture.Sql);
}

[ConditionalFact]
public void DateTimeOffset_LocalDateTime()
{
using var context = Fixture.CreateContext();
SetSessionTimeZone(context);
Fixture.ClearSql();

var metalContainer = context.Set<Model.Container>()
.Where(c => c.DeliveredDateTimeOffset.LocalDateTime == MySqlTimeZoneFixture.OriginalDateTimeUtc.AddHours(MySqlTimeZoneFixture.SessionOffset))
.Select(c => new { c.DeliveredDateTimeOffset.LocalDateTime })
.Single();

Assert.Equal(MySqlTimeZoneFixture.OriginalDateTimeUtc.AddHours(MySqlTimeZoneFixture.SessionOffset), metalContainer.LocalDateTime);

Assert.Equal(
"""
SELECT CONVERT_TZ(`c`.`DeliveredDateTimeOffset`, '+00:00', @@session.time_zone) AS `LocalDateTime`
FROM `Container` AS `c`
WHERE CONVERT_TZ(`c`.`DeliveredDateTimeOffset`, '+00:00', @@session.time_zone) = TIMESTAMP '2023-12-31 15:00:00'
LIMIT 2
""",
Fixture.Sql);
}

private static void SetSessionTimeZone(MySqlTimeZoneFixture.MySqlTimeZoneContext context)
{
context.Database.OpenConnection();
Expand All @@ -63,6 +87,7 @@ private static void SetSessionTimeZone(MySqlTimeZoneFixture.MySqlTimeZoneContext

public class MySqlTimeZoneFixture : MySqlTestFixtureBase<MySqlTimeZoneFixture.MySqlTimeZoneContext>
{
public const int SessionOffset = -8; // UTC-8
public const int OriginalOffset = 2; // UTC+2
public static readonly DateTime OriginalDateTimeUtc = new DateTime(2023, 12, 31, 23, 0, 0);
public static readonly DateTime OriginalDateTime = OriginalDateTimeUtc.AddHours(OriginalOffset);
Expand Down

0 comments on commit b650ad7

Please sign in to comment.