diff --git a/src/EFCore.MySql/Query/Internal/MySqlDateTimeMethodTranslator.cs b/src/EFCore.MySql/Query/Internal/MySqlDateTimeMethodTranslator.cs index 4fbd1d39f..cff98cabb 100644 --- a/src/EFCore.MySql/Query/Internal/MySqlDateTimeMethodTranslator.cs +++ b/src/EFCore.MySql/Query/Internal/MySqlDateTimeMethodTranslator.cs @@ -21,14 +21,14 @@ public class MySqlDateTimeMethodTranslator : IMethodCallTranslator { typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddHours), new[] { typeof(double) }), "hour" }, { typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddMinutes), new[] { typeof(double) }), "minute" }, { typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddSeconds), new[] { typeof(double) }), "second" }, - { typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddMilliseconds), new[] { typeof(double) }), "microsecond" }, + { typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddMilliseconds), new[] { typeof(double) }), "millisecond" }, { typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddYears), new[] { typeof(int) }), "year" }, { typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddMonths), new[] { typeof(int) }), "month" }, { typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddDays), new[] { typeof(double) }), "day" }, { typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddHours), new[] { typeof(double) }), "hour" }, { typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddMinutes), new[] { typeof(double) }), "minute" }, { typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddSeconds), new[] { typeof(double) }), "second" }, - { typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddMilliseconds), new[] { typeof(double) }), "microsecond" }, + { typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddMilliseconds), new[] { typeof(double) }), "millisecond" }, { typeof(DateOnly).GetRuntimeMethod(nameof(DateOnly.AddYears), new[] { typeof(int) }), "year" }, { typeof(DateOnly).GetRuntimeMethod(nameof(DateOnly.AddMonths), new[] { typeof(int) }), "month" }, { typeof(DateOnly).GetRuntimeMethod(nameof(DateOnly.AddDays), new[] { typeof(int) }), "day" }, @@ -36,6 +36,12 @@ public class MySqlDateTimeMethodTranslator : IMethodCallTranslator { typeof(TimeOnly).GetRuntimeMethod(nameof(TimeOnly.AddMinutes), new[] { typeof(double) }), "minute" }, }; + private static readonly Dictionary _methodInfoDateDiffMapping = new() + { + { typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.ToUnixTimeSeconds), Type.EmptyTypes)!, "second" }, + { typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.ToUnixTimeMilliseconds), Type.EmptyTypes)!, "millisecond" } + }; + private static readonly MethodInfo _timeOnlyAddTimeSpanMethod = typeof(TimeOnly).GetRuntimeMethod(nameof(TimeOnly.Add), new[] { typeof(TimeSpan) })!; private static readonly MethodInfo _timeOnlyIsBetweenMethod = typeof(TimeOnly).GetRuntimeMethod(nameof(TimeOnly.IsBetween), new[] { typeof(TimeOnly), typeof(TimeOnly) })!; @@ -70,12 +76,15 @@ public virtual SqlExpression Translate( new SqlExpression[] { _sqlExpressionFactory.Fragment("INTERVAL"), - datePart.Equals("microsecond") + datePart.Equals("millisecond") ? _sqlExpressionFactory.Multiply( _sqlExpressionFactory.Constant(1000), _sqlExpressionFactory.Convert(arguments[0], typeof(int))) : _sqlExpressionFactory.Convert(arguments[0], typeof(int)), - _sqlExpressionFactory.Fragment(datePart) + _sqlExpressionFactory.Fragment( + datePart == "millisecond" + ? "microsecond" + : datePart) }, " ", typeof(string)) @@ -86,6 +95,35 @@ public virtual SqlExpression Translate( new[] {true, false}); } + if (method.DeclaringType == typeof(DateTimeOffset) && + instance is not null) + { + if (_methodInfoDateDiffMapping.TryGetValue(method, out var timePart)) + { + SqlExpression expression = _sqlExpressionFactory.NullableFunction( + "TIMESTAMPDIFF", + new[] + { + _sqlExpressionFactory.Fragment(timePart == "millisecond" ? "microsecond" : timePart), + _sqlExpressionFactory.Constant(DateTimeOffset.UnixEpoch, instance!.TypeMapping), + instance + }, + typeof(long), + typeMapping: null, + onlyNullWhenAnyNullPropagatingArgumentIsNull: true, + argumentsPropagateNullability: new[] { false, true, true }); + + if (timePart == "millisecond") + { + expression = _sqlExpressionFactory.MySqlIntegerDivide( + expression, + _sqlExpressionFactory.Constant(1_000)); + } + + return expression; + } + } + if (method.DeclaringType == typeof(TimeOnly)) { if (method == _timeOnlyAddTimeSpanMethod)