diff --git a/QueryKit.UnitTests/FilterParserTests.cs b/QueryKit.UnitTests/FilterParserTests.cs index fc9823f..f0235dd 100644 --- a/QueryKit.UnitTests/FilterParserTests.cs +++ b/QueryKit.UnitTests/FilterParserTests.cs @@ -273,7 +273,7 @@ public void can_handle_datetime_comparison_with_timezone() { var input = """SpecificDate == 2022-07-01T00:00:03+01:00"""; var filterExpression = FilterParser.ParseFilter(input); - filterExpression.ToDisplayString().Should().Be("x => (x.SpecificDate == new Nullable`1(new DateTimeOffset(637922304030000000, 01:00:00)))"); + filterExpression.ToDisplayString().Should().Be("x => (x.SpecificDate == new Nullable`1(new DateTimeOffset(637922268030000000, 00:00:00)))"); } [Theory] @@ -308,7 +308,7 @@ public void can_handle_datetime_comparison_with_timezone_another() { var input = """SpecificDate == "2022-07-01T00:00:03+01:00" """; var filterExpression = FilterParser.ParseFilter(input); - filterExpression.ToDisplayString().Should().Be("x => (x.SpecificDate == new Nullable`1(new DateTimeOffset(637922304030000000, 01:00:00)))"); + filterExpression.ToDisplayString().Should().Be("x => (x.SpecificDate == new Nullable`1(new DateTimeOffset(637922268030000000, 00:00:00)))"); } [Fact] @@ -324,7 +324,7 @@ public void can_handle_datetime_comparison_with_negative_timezone() { var input = """SpecificDate == 2022-07-01T00:00:03-02:00"""; var filterExpression = FilterParser.ParseFilter(input); - filterExpression.ToDisplayString().Should().Be("x => (x.SpecificDate == new Nullable`1(new DateTimeOffset(637922304030000000, -02:00:00)))"); + filterExpression.ToDisplayString().Should().Be("x => (x.SpecificDate == new Nullable`1(new DateTimeOffset(637922376030000000, 00:00:00)))"); } [Fact] @@ -332,7 +332,7 @@ public void can_handle_datetime_comparison_with_timezone_no_minutes() { var input = """SpecificDate == 2022-07-01T00:00:03+02"""; var filterExpression = FilterParser.ParseFilter(input); - filterExpression.ToDisplayString().Should().Be("x => (x.SpecificDate == new Nullable`1(new DateTimeOffset(637922304030000000, 02:00:00)))"); + filterExpression.ToDisplayString().Should().Be("x => (x.SpecificDate == new Nullable`1(new DateTimeOffset(637922232030000000, 00:00:00)))"); } [Fact] diff --git a/QueryKit/FilterParser.cs b/QueryKit/FilterParser.cs index 5b02bda..d12d118 100644 --- a/QueryKit/FilterParser.cs +++ b/QueryKit/FilterParser.cs @@ -413,11 +413,6 @@ from trailingSpaces in Parse.WhiteSpace.Many() select LogicalOperator.GetByOperatorString(match.Operator))); } - // Npgsql only accepts a DateTimeOffset parameter with offset 0, so a parameter gets the same instant in UTC. - // A literal keeps its offset, like v1.14.2. - private static DateTimeOffset ToParameterOffset(DateTimeOffset value) - => FilterValue.Parameterize ? value.ToUniversalTime() : value; - private static readonly Dictionary> TypeConversionFunctions = new() { { typeof(string), value => value }, @@ -432,7 +427,7 @@ private static DateTimeOffset ToParameterOffset(DateTimeOffset value) { typeof(short), value => short.Parse(value, CultureInfo.InvariantCulture) }, { typeof(byte), value => byte.Parse(value, CultureInfo.InvariantCulture) }, { typeof(DateTime), value => DateTime.Parse(value, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal) }, - { typeof(DateTimeOffset), value => ToParameterOffset(DateTimeOffset.Parse(value)) }, + { typeof(DateTimeOffset), value => DateTimeOffset.Parse(value).ToUniversalTime() }, { typeof(DateOnly), value => DateOnly.Parse(value) }, { typeof(TimeOnly), value => TimeOnly.Parse(value) }, { typeof(TimeSpan), value => TimeSpan.Parse(value) }, @@ -635,7 +630,8 @@ private static Expression CreateRightExprFromType(Type leftExprType, string righ { var dtStyle = right.EndsWith("Z") ? DateTimeStyles.AdjustToUniversal : DateTimeStyles.AssumeLocal; var dto = DateTimeOffset.Parse(right, CultureInfo.InvariantCulture, dtStyle); - return FilterValue.Create(ToParameterOffset(dto), rawType); + // Npgsql only accepts a DateTimeOffset parameter with offset 0. The UTC value is the same instant. + return FilterValue.Create(dto.ToUniversalTime(), rawType); } if (targetType == typeof(DateOnly)) diff --git a/README.md b/README.md index 36ad5a1..ff616da 100644 --- a/README.md +++ b/README.md @@ -251,6 +251,7 @@ Property list grouping is particularly useful for: * `DateOnly`: `var filterInput = """Birthday == "2022-07-01" """;` * `DateTimeOffset`: * `var filterInput = """Birthday == "2022-07-01T00:00:03Z" """;` + * QueryKit converts a `DateTimeOffset` value to UTC. `"2022-07-01T00:00:03+01:00"` becomes the same instant with offset 0: `2022-06-30T23:00:03Z`. * `DateTime`: `var filterInput = """Birthday == "2022-07-01" """;` * `var filterInput = """Birthday == "2022-07-01T00:00:03" """;` * `var filterInput = """Birthday == "2022-07-01T00:00:03+01:00" """;`