diff --git a/src/Redis.OM/Modeling/RedisCollectionStateManager.cs b/src/Redis.OM/Modeling/RedisCollectionStateManager.cs index 20ec8eb..491495b 100644 --- a/src/Redis.OM/Modeling/RedisCollectionStateManager.cs +++ b/src/Redis.OM/Modeling/RedisCollectionStateManager.cs @@ -12,9 +12,13 @@ namespace Redis.OM.Modeling /// public class RedisCollectionStateManager { - private static JsonSerializerSettings _jsonSerializerSettings = new JsonSerializerSettings + private static readonly JsonSerializerSettings _jsonSerializerSettings = new JsonSerializerSettings { - NullValueHandling = NullValueHandling.Ignore, Converters = new List { new DateTimeJsonConvertNewtonsoft() }, + NullValueHandling = NullValueHandling.Ignore, + DateFormatHandling = DateFormatHandling.IsoDateFormat, + DateParseHandling = DateParseHandling.DateTimeOffset, + DateTimeZoneHandling = DateTimeZoneHandling.Utc, + Converters = new List { new DateTimeJsonConvertNewtonsoft() }, }; /// @@ -82,7 +86,7 @@ internal void InsertIntoSnapshot(string key, object value) if (DocumentAttribute.StorageType == StorageType.Json) { - var json = JToken.FromObject(value, Newtonsoft.Json.JsonSerializer.Create(new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, Converters = new List { new DateTimeJsonConvertNewtonsoft() } })); + var json = JToken.FromObject(value, Newtonsoft.Json.JsonSerializer.Create(_jsonSerializerSettings)); Snapshot.Add(key, json); } else @@ -110,7 +114,7 @@ internal bool TryDetectDifferencesSingle(string key, object value, out IList(dataJson, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, DateFormatHandling = DateFormatHandling.IsoDateFormat, DateParseHandling = DateParseHandling.DateTimeOffset, DateTimeZoneHandling = DateTimeZoneHandling.Utc }); + var current = JsonConvert.DeserializeObject(dataJson, _jsonSerializerSettings); var snapshot = (JToken)Snapshot[key]; var diff = FindDiff(current!, snapshot); differences = BuildJsonDifference(diff, "$", snapshot); @@ -146,7 +150,7 @@ internal IDictionary> DetectDifferences() if (Data.ContainsKey(key)) { var dataJson = JsonSerializer.Serialize(Data[key], RedisSerializationSettings.JsonSerializerOptions); - var current = JsonConvert.DeserializeObject(dataJson, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); + var current = JsonConvert.DeserializeObject(dataJson, _jsonSerializerSettings); var snapshot = (JToken)Snapshot[key]; var diff = FindDiff(current!, snapshot); var diffArgs = BuildJsonDifference(diff, "$", snapshot); diff --git a/test/Redis.OM.Unit.Tests/RediSearchTests/SearchTests.cs b/test/Redis.OM.Unit.Tests/RediSearchTests/SearchTests.cs index 6eb09b9..bc2b6b1 100644 --- a/test/Redis.OM.Unit.Tests/RediSearchTests/SearchTests.cs +++ b/test/Redis.OM.Unit.Tests/RediSearchTests/SearchTests.cs @@ -60,6 +60,18 @@ public class SearchTests }) }; + private readonly RedisReply _mockedReplyObjectWithDateTimeOffset = new[] + { + new RedisReply(1), + new RedisReply( + "Redis.OM.Unit.Tests.ObjectWithDateTimeOffsetJson:01FVN836BNQGYMT80V7RCVY73N"), + new RedisReply(new RedisReply[] + { + "$", + "{\"Id\":\"01FVN836BNQGYMT80V7RCVY73N\",\"DateTime\":1729592130000,\"Offset\":\"2024-12-25T00:00:00.000+01:00\"}" + }) + }; + private readonly RedisReply _mockedReplyObjectWIthMultipleByteArrays = new[] { new RedisReply(1), @@ -1087,6 +1099,38 @@ public async Task TestUpdateJsonWithMultipleDateTimes() Scripts.ShaCollection.Clear(); } + [Fact] + public async Task TestSaveJsonWithDateTimeOffset() + { + _substitute.ExecuteAsync("FT.SEARCH", Arg.Any()).Returns(_mockedReplyObjectWithDateTimeOffset); + + _substitute.ExecuteAsync("EVALSHA", Arg.Any()).Returns(Task.FromResult(new RedisReply("42"))); + _substitute.ExecuteAsync("SCRIPT", Arg.Any()) + .Returns(Task.FromResult(new RedisReply("cbbf1c4fab5064f419e469cc51c563f8bf51e6fb"))); + var collection = new RedisCollection(_substitute); + var obj = (await collection.Where(x => x.Id == "01FVN836BNQGYMT80V7RCVY73N").ToListAsync()).First(); + obj.DateTime = obj.DateTime.AddMilliseconds(1); + await collection.SaveAsync(); + await _substitute.Received().ExecuteAsync("EVALSHA", Arg.Any(), "1", new RedisKey("Redis.OM.Unit.Tests.ObjectWithDateTimeOffsetJson:01FVN836BNQGYMT80V7RCVY73N"), "SET", "$.DateTime", "1729592130001"); + Scripts.ShaCollection.Clear(); + } + + [Fact] + public async Task TestUpdateJsonWithDateTimeOffset() + { + _substitute.ExecuteAsync("FT.SEARCH", Arg.Any()).Returns(_mockedReplyObjectWithDateTimeOffset); + + _substitute.ExecuteAsync("EVALSHA", Arg.Any()).Returns(Task.FromResult(new RedisReply("42"))); + _substitute.ExecuteAsync("SCRIPT", Arg.Any()) + .Returns(Task.FromResult(new RedisReply("cbbf1c4fab5064f419e469cc51c563f8bf51e6fb"))); + var collection = new RedisCollection(_substitute); + var obj = (await collection.Where(x => x.Id == "01FVN836BNQGYMT80V7RCVY73N").ToListAsync()).First(); + obj.DateTime = obj.DateTime.AddMilliseconds(1); + await collection.UpdateAsync(obj); + await _substitute.Received().ExecuteAsync("EVALSHA", Arg.Any(), "1", new RedisKey("Redis.OM.Unit.Tests.ObjectWithDateTimeOffsetJson:01FVN836BNQGYMT80V7RCVY73N"), "SET", "$.DateTime", "1729592130001"); + Scripts.ShaCollection.Clear(); + } + [Fact] public async Task TestUpdateJsonWithMultipleDateTimesHash()