From e402fa70d7cb04f0f045e6604b714ce156053993 Mon Sep 17 00:00:00 2001 From: VitaliyChaban <95292018+VitaliyChaban@users.noreply.github.com> Date: Thu, 9 Jan 2025 18:40:36 +0200 Subject: [PATCH] Fix/datetime filter fix (#1316) * datetime_filter_fix * comment * minor refactor * minor refactor * fix: set behavior depending on timeZoneInfo * minor cleanup * Update src/Microsoft.AspNetCore.OData/Edm/EdmPrimitiveHelper.cs minor refactor Co-authored-by: Samuel Wanjohi * check utc first * improve tests * Implicit test fix * Implicit test fix * add filter by dateTime test * Revert "add filter by dateTime test" This reverts commit 34b9ef8302a291afaa2ff3dfff4baca46493ab39. --------- Co-authored-by: Samuel Wanjohi --- .../Edm/EdmPrimitiveHelper.cs | 19 ++++++- .../Edm/EdmPrimitiveHelperTests.cs | 50 ++++++++++++++++++- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.AspNetCore.OData/Edm/EdmPrimitiveHelper.cs b/src/Microsoft.AspNetCore.OData/Edm/EdmPrimitiveHelper.cs index 33e4cf031..7173891c6 100644 --- a/src/Microsoft.AspNetCore.OData/Edm/EdmPrimitiveHelper.cs +++ b/src/Microsoft.AspNetCore.OData/Edm/EdmPrimitiveHelper.cs @@ -105,8 +105,12 @@ public static object ConvertPrimitiveValue(object value, Type type, TimeZoneInfo { DateTimeOffset dateTimeOffsetValue = (DateTimeOffset)value; TimeZoneInfo timeZone = timeZoneInfo ?? TimeZoneInfo.Local; + dateTimeOffsetValue = TimeZoneInfo.ConvertTime(dateTimeOffsetValue, timeZone); - return dateTimeOffsetValue.DateTime; + + DateTimeKind dateTimeKind = GetTargetDateTimeKind(timeZone); + + return DateTime.SpecifyKind(dateTimeOffsetValue.DateTime, dateTimeKind); } if (value is Date) @@ -183,4 +187,15 @@ public static object ConvertPrimitiveValue(object value, Type type, TimeZoneInfo } } } -} + + private static DateTimeKind GetTargetDateTimeKind(TimeZoneInfo timeZone) + { + if (timeZone.Equals(TimeZoneInfo.Utc)) + return DateTimeKind.Utc; + + if (timeZone.Equals(TimeZoneInfo.Local)) + return DateTimeKind.Local; + + return DateTimeKind.Unspecified; + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.OData.Tests/Edm/EdmPrimitiveHelperTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Edm/EdmPrimitiveHelperTests.cs index a3f6c63d6..745388fbb 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Edm/EdmPrimitiveHelperTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Edm/EdmPrimitiveHelperTests.cs @@ -94,7 +94,7 @@ public static TheoryDataSet ConvertDateTime_NonStandardPrimitive [Theory] [MemberData(nameof(ConvertPrimitiveValue_NonStandardPrimitives_Data))] - [MemberData(nameof(ConvertPrimitiveValue_NonStandardPrimitives_ExtraData))] + [MemberData(nameof(ConvertPrimitiveValue_NonStandardPrimitives_ExtraData))] public void ConvertPrimitiveValue_NonStandardPrimitives(object valueToConvert, object result, Type conversionType) { // Arrange & Act @@ -124,6 +124,52 @@ public void ConvertDateTimeValue_NonStandardPrimitives_DefaultTimeZoneInfo(DateT Assert.Equal(valueToConvert.LocalDateTime, dt); } + [Theory] + [MemberData(nameof(ConvertDateTime_NonStandardPrimitives_Data))] + public void ConvertDateTimeValue_ImplicitKind(DateTimeOffset valueToConvert) + { + // Arrange & Act + object actual = EdmPrimitiveHelper.ConvertPrimitiveValue(valueToConvert, typeof(DateTime)); + + //if server local time is UTC, then expect Utc Kind + DateTimeKind expectedTimeKind = TimeZoneInfo.Local.Equals(TimeZoneInfo.Utc) + ? DateTimeKind.Utc + : DateTimeKind.Local; + + // Assert + DateTime dt = Assert.IsType(actual); + Assert.Equal(expectedTimeKind, dt.Kind); + } + + [Theory] + [MemberData(nameof(ConvertDateTime_NonStandardPrimitives_Data))] + public void ConvertDateTimeValue_ExplicitLocalKind(DateTimeOffset valueToConvert) + { + // Arrange & Act + object actual = EdmPrimitiveHelper.ConvertPrimitiveValue(valueToConvert, typeof(DateTime), TimeZoneInfo.Local); + + //if server local time is UTC, then expect Utc Kind + DateTimeKind expectedTimeKind = TimeZoneInfo.Local.Equals(TimeZoneInfo.Utc) + ? DateTimeKind.Utc + : DateTimeKind.Local; + + // Assert + DateTime dt = Assert.IsType(actual); + Assert.Equal(expectedTimeKind, dt.Kind); + } + + [Theory] + [MemberData(nameof(ConvertDateTime_NonStandardPrimitives_Data))] + public void ConvertDateTimeValue_ExplicitUtcKind(DateTimeOffset valueToConvert) + { + // Arrange & Act + object actual = EdmPrimitiveHelper.ConvertPrimitiveValue(valueToConvert, typeof(DateTime), TimeZoneInfo.Utc); + + // Assert + DateTime dt = Assert.IsType(actual); + Assert.Equal(DateTimeKind.Utc, dt.Kind); + } + [Theory] [MemberData(nameof(ConvertDateTime_NonStandardPrimitives_Data))] public void ConvertDateTimeValue_NonStandardPrimitives_CustomTimeZoneInfo(DateTimeOffset valueToConvert) @@ -146,4 +192,4 @@ public void ConvertPrimitiveValue_Throws(object valueToConvert, Type conversionT () => EdmPrimitiveHelper.ConvertPrimitiveValue(valueToConvert, conversionType), exception); } -} +} \ No newline at end of file