diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteMemberTranslatorProvider.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteMemberTranslatorProvider.cs
index 25adce48e5f..e914fd1ebbb 100644
--- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteMemberTranslatorProvider.cs
+++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteMemberTranslatorProvider.cs
@@ -26,7 +26,8 @@ public SqliteMemberTranslatorProvider(RelationalMemberTranslatorProviderDependen
[
new SqliteDateTimeMemberTranslator(sqlExpressionFactory),
new SqliteStringLengthTranslator(sqlExpressionFactory),
- new SqliteDateOnlyMemberTranslator(sqlExpressionFactory)
+ new SqliteDateOnlyMemberTranslator(sqlExpressionFactory),
+ new SqliteTimeOnlyMemberTranslator(sqlExpressionFactory)
]);
}
}
diff --git a/src/EFCore.Sqlite.Core/Query/Internal/Translators/SqliteTimeOnlyMemberTranslator.cs b/src/EFCore.Sqlite.Core/Query/Internal/Translators/SqliteTimeOnlyMemberTranslator.cs
new file mode 100644
index 00000000000..6905bd78003
--- /dev/null
+++ b/src/EFCore.Sqlite.Core/Query/Internal/Translators/SqliteTimeOnlyMemberTranslator.cs
@@ -0,0 +1,51 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+
+// ReSharper disable once CheckNamespace
+namespace Microsoft.EntityFrameworkCore.Sqlite.Query.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class SqliteTimeOnlyMemberTranslator(SqliteSqlExpressionFactory sqlExpressionFactory) : IMemberTranslator
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual SqlExpression? Translate(
+ SqlExpression? instance,
+ MemberInfo member,
+ Type returnType,
+ IDiagnosticsLogger logger)
+ {
+ if (member.DeclaringType != typeof(TimeOnly) || instance is null)
+ {
+ return null;
+ }
+
+ return member.Name switch
+ {
+ nameof(TimeOnly.Hour) => DatePart("%H"),
+ nameof(TimeOnly.Minute) => DatePart("%M"),
+ nameof(TimeOnly.Second) => DatePart("%S"),
+
+ _ => null
+ };
+
+ SqlExpression DatePart(string datePart)
+ => sqlExpressionFactory.Convert(
+ sqlExpressionFactory.Strftime(
+ typeof(string),
+ datePart,
+ instance),
+ returnType);
+ }
+}
diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Translations/Temporal/TimeOnlyTranslationsSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Translations/Temporal/TimeOnlyTranslationsSqliteTest.cs
index e784c3e03b2..eae438fecd7 100644
--- a/test/EFCore.Sqlite.FunctionalTests/Query/Translations/Temporal/TimeOnlyTranslationsSqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/Query/Translations/Temporal/TimeOnlyTranslationsSqliteTest.cs
@@ -14,26 +14,38 @@ public TimeOnlyTranslationsSqliteTest(BasicTypesQuerySqliteFixture fixture, ITes
public override async Task Hour()
{
- // TimeSpan. Issue #18844.
- await AssertTranslationFailed(() => base.Hour());
+ await base.Hour();
- AssertSql();
+ AssertSql(
+ """
+SELECT "b"."Id", "b"."Bool", "b"."Byte", "b"."ByteArray", "b"."DateOnly", "b"."DateTime", "b"."DateTimeOffset", "b"."Decimal", "b"."Double", "b"."Enum", "b"."FlagsEnum", "b"."Float", "b"."Guid", "b"."Int", "b"."Long", "b"."Short", "b"."String", "b"."TimeOnly", "b"."TimeSpan"
+FROM "BasicTypesEntities" AS "b"
+WHERE CAST(strftime('%H', "b"."TimeOnly") AS INTEGER) = 15
+""");
}
public override async Task Minute()
{
- // TimeSpan. Issue #18844.
- await AssertTranslationFailed(() => base.Minute());
+ await base.Minute();
- AssertSql();
+ AssertSql(
+ """
+SELECT "b"."Id", "b"."Bool", "b"."Byte", "b"."ByteArray", "b"."DateOnly", "b"."DateTime", "b"."DateTimeOffset", "b"."Decimal", "b"."Double", "b"."Enum", "b"."FlagsEnum", "b"."Float", "b"."Guid", "b"."Int", "b"."Long", "b"."Short", "b"."String", "b"."TimeOnly", "b"."TimeSpan"
+FROM "BasicTypesEntities" AS "b"
+WHERE CAST(strftime('%M', "b"."TimeOnly") AS INTEGER) = 30
+""");
}
public override async Task Second()
{
- // TimeSpan. Issue #18844.
- await AssertTranslationFailed(() => base.Second());
+ await base.Second();
- AssertSql();
+ AssertSql(
+ """
+SELECT "b"."Id", "b"."Bool", "b"."Byte", "b"."ByteArray", "b"."DateOnly", "b"."DateTime", "b"."DateTimeOffset", "b"."Decimal", "b"."Double", "b"."Enum", "b"."FlagsEnum", "b"."Float", "b"."Guid", "b"."Int", "b"."Long", "b"."Short", "b"."String", "b"."TimeOnly", "b"."TimeSpan"
+FROM "BasicTypesEntities" AS "b"
+WHERE CAST(strftime('%S', "b"."TimeOnly") AS INTEGER) = 10
+""");
}
public override async Task Millisecond()