diff --git a/src/EFCore.PG/Extensions/DbFunctionsExtensions/NpgsqlDbFunctionsExtensions.cs b/src/EFCore.PG/Extensions/DbFunctionsExtensions/NpgsqlDbFunctionsExtensions.cs index 9488c8ec8..15cf1ae2f 100644 --- a/src/EFCore.PG/Extensions/DbFunctionsExtensions/NpgsqlDbFunctionsExtensions.cs +++ b/src/EFCore.PG/Extensions/DbFunctionsExtensions/NpgsqlDbFunctionsExtensions.cs @@ -141,4 +141,24 @@ public static int Distance(this DbFunctions _, DateOnly a, DateOnly b) /// public static TimeSpan Distance(this DbFunctions _, DateTime a, DateTime b) => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Distance))); + + /// + /// Converts string to date according to the given format. + /// + /// The instance. + /// The string to be converted. + /// The format of the input date. + /// + public static DateOnly? ToDate(this DbFunctions _, string? value, string? format) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(ToDate))); + + /// + /// Converts string to time stamp according to the given format. + /// + /// The instance. + /// The string to be converted + /// The format of the input date + /// + public static DateTime? ToTimestamp(this DbFunctions _, string? value, string? format) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(ToTimestamp))); } diff --git a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlStringMethodTranslator.cs b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlStringMethodTranslator.cs index 3a97d91e8..4cf7a59de 100644 --- a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlStringMethodTranslator.cs +++ b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlStringMethodTranslator.cs @@ -78,6 +78,12 @@ public class NpgsqlStringMethodTranslator : IMethodCallTranslator private static readonly MethodInfo StringToArrayNullString = typeof(NpgsqlDbFunctionsExtensions).GetRuntimeMethod( nameof(NpgsqlDbFunctionsExtensions.StringToArray), [typeof(DbFunctions), typeof(string), typeof(string), typeof(string)])!; + private static readonly MethodInfo ToDate = typeof(NpgsqlDbFunctionsExtensions).GetRuntimeMethod( + nameof(NpgsqlDbFunctionsExtensions.ToDate), [typeof(DbFunctions), typeof(string), typeof(string)])!; + + private static readonly MethodInfo ToTimestamp = typeof(NpgsqlDbFunctionsExtensions).GetRuntimeMethod( + nameof(NpgsqlDbFunctionsExtensions.ToTimestamp), [typeof(DbFunctions), typeof(string), typeof(string)])!; + private static readonly MethodInfo FirstOrDefaultMethodInfoWithoutArgs = typeof(Enumerable).GetRuntimeMethods().Single( m => m.Name == nameof(Enumerable.FirstOrDefault) @@ -425,6 +431,30 @@ public NpgsqlStringMethodTranslator(NpgsqlTypeMappingSource typeMappingSource, I _typeMappingSource.FindMapping(typeof(string[]))); } + if (method == ToDate) + { + return _sqlExpressionFactory.Function( + "to_date", + new[] { arguments[1], arguments[2] }, + nullable: true, + argumentsPropagateNullability: new[] { true, true }, + typeof(DateOnly?), + _typeMappingSource.FindMapping(typeof(DateOnly)) + ); + } + + if (method == ToTimestamp) + { + return _sqlExpressionFactory.Function( + "to_timestamp", + new[] { arguments[1], arguments[2] }, + nullable: true, + argumentsPropagateNullability: new[] { true, true }, + typeof(DateTime?), + _typeMappingSource.FindMapping(typeof(DateTime)) + ); + } + return null; } diff --git a/test/EFCore.PG.FunctionalTests/Query/NorthwindDbFunctionsQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/NorthwindDbFunctionsQueryNpgsqlTest.cs index 46d03dc14..4b9f7a5da 100644 --- a/test/EFCore.PG.FunctionalTests/Query/NorthwindDbFunctionsQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/NorthwindDbFunctionsQueryNpgsqlTest.cs @@ -227,6 +227,34 @@ WHERE string_to_array(c."ContactName", ' ', 'Maria') = ARRAY[NULL,'Anders']::tex """); } + [Fact] + public void ToDate() + { + using var context = CreateContext(); + var count = context.Orders.Count(c => EF.Functions.ToDate(c.OrderDate.ToString(), "YYYY-MM-DD") < new DateOnly(2000, 01, 01)); + Assert.Equal(830, count); + AssertSql( +""" +SELECT count(*)::int +FROM "Orders" AS o +WHERE to_date(o."OrderDate"::text, 'YYYY-MM-DD') < DATE '2000-01-01' +"""); + } + + [Fact] + public void ToTimestamp() + { + using var context = CreateContext(); + var count = context.Orders.Count(c => EF.Functions.ToTimestamp(c.OrderDate.ToString(), "YYYY-MM-DD") < new DateTime(2000, 01, 01, 0,0,0, DateTimeKind.Utc)); + Assert.Equal(830, count); + AssertSql( + """ +SELECT count(*)::int +FROM "Orders" AS o +WHERE to_timestamp(o."OrderDate"::text, 'YYYY-MM-DD') < TIMESTAMPTZ '2000-01-01T00:00:00Z' +"""); + } + #endregion private void AssertSql(params string[] expected)