Skip to content

Commit

Permalink
Redo timestamp handling
Browse files Browse the repository at this point in the history
Closes npgsql#1970
Closes npgsql#873
Closes npgsql#473
Closes npgsql#1974
Closes npgsql#1983
  • Loading branch information
roji committed Sep 19, 2021
1 parent 7936414 commit 6580c8f
Show file tree
Hide file tree
Showing 69 changed files with 3,835 additions and 942 deletions.
7 changes: 7 additions & 0 deletions EFCore.PG.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ Licensed under the Apache License, Version 2.0. See License.txt in the project r
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EXml_002ECodeStyle_002EFormatSettingsUpgrade_002EXmlMoveToCommonFormatterSettingsUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=annotatable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=composable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=daterange/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=datetimeoffset/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=doesnt/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=fallbacks/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=initializers/@EntryIndexedValue">True</s:Boolean>
Expand All @@ -228,11 +230,16 @@ Licensed under the Apache License, Version 2.0. See License.txt in the project r
<s:Boolean x:Key="/Default/UserDictionary/Words/=sproc/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=sqlite/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=subquery/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=timestamptz/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=timetz/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=tsrange/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=tstzrange/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=unignore/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=fixup/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=attacher/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Uniquify/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Unlogged/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=utcnow/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Xunit/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

<!-- From here it's EFCore.PG stuff -->
Expand Down
8 changes: 8 additions & 0 deletions src/EFCore.PG.NodaTime/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime.FunctionalTests, PublicKey=" +
"0024000004800000940000000602000000240000525341310004000001000100" +
"2b3c590b2a4e3d347e6878dc0ff4d21eb056a50420250c6617044330701d35c9" +
"8078a5df97a62d83c9a2db2d072523a8fc491398254c6b89329b8c1dcef43a1e" +
"7aa16153bcea2ae9a471145624826f60d7c8e71cd025b554a0177bd935a78096" +
"29f0a7afc778ebb4ad033e1bf512c1a9c6ceea26b077bc46cac93800435e77ee")]

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using NodaTime;
using Npgsql.EntityFrameworkCore.PostgreSQL.Query;

// ReSharper disable once CheckNamespace
namespace Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime
namespace Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime.Query.Internal
{
/// <summary>
/// Provides translation services for <see cref="NodaTime"/> members.
Expand All @@ -22,30 +22,23 @@ public NpgsqlNodaTimeMemberTranslatorPlugin(ISqlExpressionFactory sqlExpressionF
{
Translators = new IMemberTranslator[]
{
new NpgsqlNodaTimeMemberTranslator(sqlExpressionFactory),
new NpgsqlNodaTimeMemberTranslator((NpgsqlSqlExpressionFactory)sqlExpressionFactory),
};
}

public virtual IEnumerable<IMemberTranslator> Translators { get; }
}

/// <summary>
/// Provides translation services for <see cref="NodaTime"/> members.
/// </summary>
/// <remarks>
/// See: https://www.postgresql.org/docs/current/static/functions-datetime.html
/// </remarks>
public class NpgsqlNodaTimeMemberTranslator : IMemberTranslator
{
private readonly ISqlExpressionFactory _sqlExpressionFactory;
private readonly NpgsqlSqlExpressionFactory _sqlExpressionFactory;

/// <summary>
/// The static member info for <see cref="T:SystemClock.Instance"/>.
/// </summary>
private static readonly MemberInfo Instance =
private static readonly MemberInfo SystemClock_Instance =
typeof(SystemClock).GetRuntimeProperty(nameof(SystemClock.Instance))!;
private static readonly MemberInfo ZonedDateTime_LocalDateTime =
typeof(ZonedDateTime).GetRuntimeProperty(nameof(ZonedDateTime.LocalDateTime))!;

public NpgsqlNodaTimeMemberTranslator(ISqlExpressionFactory sqlExpressionFactory)
public NpgsqlNodaTimeMemberTranslator(NpgsqlSqlExpressionFactory sqlExpressionFactory)
=> _sqlExpressionFactory = sqlExpressionFactory;

private static readonly bool[][] TrueArrays =
Expand All @@ -63,24 +56,35 @@ public NpgsqlNodaTimeMemberTranslator(ISqlExpressionFactory sqlExpressionFactory
IDiagnosticsLogger<DbLoggerCategory.Query> logger)
{
// This is necessary to allow translation of methods on SystemClock.Instance
if (member == Instance)
if (member == SystemClock_Instance)
return _sqlExpressionFactory.Constant(SystemClock.Instance);

if (instance is null)
{
return null;
}

var declaringType = member.DeclaringType;
if (instance is not null)

if (declaringType == typeof(LocalDateTime)
|| declaringType == typeof(LocalDate)
|| declaringType == typeof(LocalTime)
|| declaringType == typeof(Period))
{
if (declaringType == typeof(LocalDateTime)
|| declaringType == typeof(LocalDate)
|| declaringType == typeof(LocalTime)
|| declaringType == typeof(Period))
{
return TranslateDateTime(instance, member, returnType);
}

if (declaringType == typeof(Duration))
{
return TranslateDuration(instance, member);
}
return TranslateDateTime(instance, member, returnType);
}

if (declaringType == typeof(ZonedDateTime))
{
// date_part, which is used to extract most components, doesn't have an overload for timestamptz, so passing one directly
// converts it to the local timezone as per TimeZone. Explicitly convert it to a local timestamp in UTC.
return TranslateDateTime(_sqlExpressionFactory.AtUtc(instance), member, returnType)
?? TranslateZonedDateTime(instance, member);
}

if (declaringType == typeof(Duration))
{
return TranslateDuration(instance, member);
}

return null;
Expand Down Expand Up @@ -108,13 +112,6 @@ public NpgsqlNodaTimeMemberTranslator(ISqlExpressionFactory sqlExpressionFactory
};
}

/// <summary>
/// Translates date and time members.
/// </summary>
/// <param name="e">The member expression.</param>
/// <returns>
/// The translated expression or null.
/// </returns>
private SqlExpression? TranslateDateTime(SqlExpression instance, MemberInfo member, Type returnType)
{
switch (member.Name)
Expand Down Expand Up @@ -231,5 +228,13 @@ private SqlExpression GetDatePartExpressionDouble(

return result;
}

private SqlExpression? TranslateZonedDateTime(SqlExpression instance, MemberInfo member)
{
if (member == ZonedDateTime_LocalDateTime)
return _sqlExpressionFactory.AtUtc(instance);

return null;
}
}
}
Loading

0 comments on commit 6580c8f

Please sign in to comment.