Skip to content

Commit

Permalink
Add some missing translations for DateTimeOffset
Browse files Browse the repository at this point in the history
  • Loading branch information
roji committed Nov 22, 2021
1 parent 40f29d5 commit 43cb921
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ public NpgsqlDateTimeMemberTranslator(
IDiagnosticsLogger<DbLoggerCategory.Query> logger)
{
var type = member.DeclaringType;

if (type == typeof(DateTimeOffset)
&& instance is not null
&& TranslateDateTimeOffset(instance, member, returnType) is { } translated)
{
return translated;
}

if (type != typeof(DateTime)
&& type != typeof(DateTimeOffset)
&& type != typeof(DateOnly)
Expand Down Expand Up @@ -147,4 +155,22 @@ private SqlExpression GetDatePartExpression(

return _sqlExpressionFactory.Convert(result, typeof(int));
}

public virtual SqlExpression? TranslateDateTimeOffset(
SqlExpression instance,
MemberInfo member,
Type returnType)
=> member.Name switch
{
// We only support UTC DateTimeOffset, so DateTimeOffset.DateTime is just a matter of converting to timestamp without time zone
nameof(DateTimeOffset.DateTime) => _sqlExpressionFactory.AtUtc(instance),

// We only support UTC DateTimeOffset, so DateTimeOffset.UtcDateTime does nothing (type change on CLR change, no change on the
// PG side.
nameof(DateTimeOffset.UtcDateTime) => instance,

// Convert to timestamp without time zone, applying a time zone conversion based on the TimeZone connection parameter.
nameof(DateTimeOffset.LocalDateTime) => _sqlExpressionFactory.Convert(instance, typeof(DateTime), _timestampMapping),
_ => null
};
}
62 changes: 61 additions & 1 deletion test/EFCore.PG.FunctionalTests/Query/TimestampQueryTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,66 @@ await AssertQuery(

#endregion Now

#region DateTimeOffset

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public async Task DateTimeOffset_DateTime(bool async)
{
// We only support UTC DateTimeOffset, so DateTimeOffset.DateTime is just a matter of converting to timestamp without time zone
await AssertQuery(
async,
ss => ss.Set<Entity>().Where(
e => e.TimestampDateTimeOffset.DateTime == new DateTime(1998, 4, 12, 13, 26, 38, DateTimeKind.Unspecified)),
entryCount: 1);

AssertSql(
@"SELECT e.""Id"", e.""TimestampDateTime"", e.""TimestampDateTimeArray"", e.""TimestampDateTimeOffset"", e.""TimestampDateTimeOffsetArray"", e.""TimestampDateTimeRange"", e.""TimestamptzDateTime"", e.""TimestamptzDateTimeArray"", e.""TimestamptzDateTimeRange""
FROM ""Entities"" AS e
WHERE e.""TimestampDateTimeOffset"" AT TIME ZONE 'UTC' = TIMESTAMP '1998-04-12 13:26:38'");
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public async Task DateTimeOffset_UtcDateTime(bool async)
{
// We only support UTC DateTimeOffset, so DateTimeOffset.UtcDateTime does nothing (type change on CLR change, no change on the
// PG side.
await AssertQuery(
async,
ss => ss.Set<Entity>().Where(
e => e.TimestampDateTimeOffset.UtcDateTime == new DateTime(1998, 4, 12, 13, 26, 38, DateTimeKind.Utc)),
entryCount: 1);

AssertSql(
@"SELECT e.""Id"", e.""TimestampDateTime"", e.""TimestampDateTimeArray"", e.""TimestampDateTimeOffset"", e.""TimestampDateTimeOffsetArray"", e.""TimestampDateTimeRange"", e.""TimestamptzDateTime"", e.""TimestamptzDateTimeArray"", e.""TimestamptzDateTimeRange""
FROM ""Entities"" AS e
WHERE e.""TimestampDateTimeOffset"" = TIMESTAMPTZ '1998-04-12 13:26:38Z'");
}

[ConditionalFact]
public async Task DateTimeOffset_LocalDateTime()
{
// Note that we're in the Europe/Berlin timezone (see NpgsqlTestStore below)

// We can't use AssertQuery since the local (expected) evaluation is dependent on the machine's timezone, which is out of
// our control.
await using var ctx = CreateContext();

var count = await ctx.Set<Entity>()
.Where(e => e.TimestampDateTimeOffset.LocalDateTime == new DateTime(1998, 4, 12, 15, 26, 38, DateTimeKind.Local))
.CountAsync();

Assert.Equal(1, count);

AssertSql(
@"SELECT COUNT(*)::INT
FROM ""Entities"" AS e
WHERE e.""TimestampDateTimeOffset""::timestamp = TIMESTAMP '1998-04-12 15:26:38'");
}

#endregion DateTimeOffset

#region DateTime constructors

[ConditionalTheory]
Expand Down Expand Up @@ -725,4 +785,4 @@ public static IReadOnlyList<Entity> CreateEntities()
}

#endregion
}
}

0 comments on commit 43cb921

Please sign in to comment.