diff --git a/Microsoft.Azure.Cosmos/src/Json/Interop/CosmosDBToNewtonsoftWriter.cs b/Microsoft.Azure.Cosmos/src/Json/Interop/CosmosDBToNewtonsoftWriter.cs
index f8e4e289c1..c527d3d721 100644
--- a/Microsoft.Azure.Cosmos/src/Json/Interop/CosmosDBToNewtonsoftWriter.cs
+++ b/Microsoft.Azure.Cosmos/src/Json/Interop/CosmosDBToNewtonsoftWriter.cs
@@ -328,7 +328,20 @@ public override void WriteValue(decimal value)
/// The value to write.
public override void WriteValue(DateTime value)
{
- // We use rount trip format for datetime parsing and trim the additional trailing zeros using a custom "O" format
+ // We use round trip format for datetime parsing and trim the additional trailing zeros using a custom "O" format
+ // to maintain milliseconds precision.
+ this.WriteValue(
+ value.ToString(
+ format: CosmosDBToNewtonsoftWriter.RoundTripFormatWithoutTrailingZeros));
+ }
+
+ ///
+ /// Writes a value.
+ ///
+ /// The value to write.
+ public override void WriteValue(DateTimeOffset value)
+ {
+ // We use round trip format for datetime parsing and trim the additional trailing zeros using a custom "O" format
// to maintain milliseconds precision.
this.WriteValue(
value.ToString(
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosItemUnitTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosItemUnitTests.cs
index 0f5cc0e13e..7c9291f424 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosItemUnitTests.cs
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosItemUnitTests.cs
@@ -253,6 +253,7 @@ public async Task TestGetPartitionKeyValueFromStreamAsync(bool binaryEncodingEna
cancellationToken));
DateTime dateTime = new DateTime(2019, 05, 15, 12, 1, 2, 3, DateTimeKind.Utc);
+ DateTimeOffset dateTimeOffset = new DateTimeOffset(2019, 05, 15, 12, 1, 2, 3, TimeSpan.Zero);
Guid guid = Guid.NewGuid();
//Test supported types
@@ -273,6 +274,7 @@ public async Task TestGetPartitionKeyValueFromStreamAsync(bool binaryEncodingEna
new { pk = char.MaxValue },
new { pk = "test" },
new { pk = dateTime },
+ new { pk = dateTimeOffset},
new { pk = guid },
};
@@ -308,6 +310,10 @@ public async Task TestGetPartitionKeyValueFromStreamAsync(bool binaryEncodingEna
{
Assert.AreEqual(poco.pk.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"), stringValue);
}
+ else if (poco.pk is DateTimeOffset)
+ {
+ Assert.AreEqual(poco.pk.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"), stringValue);
+ }
else
{
Assert.AreEqual(poco.pk.ToString(), (string)pk);
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/NewtonsoftInteropTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/NewtonsoftInteropTests.cs
index 42ef816e19..8b4a74a7bb 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/NewtonsoftInteropTests.cs
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/NewtonsoftInteropTests.cs
@@ -203,6 +203,7 @@ public void AllPrimitivesObjectTest()
new JProperty("boolean", true),
new JProperty("null", null),
new JProperty("datetime", DateTime.Parse("2526-07-11T18:18:16.4520716")),
+ new JProperty("datetimeoffset", DateTimeOffset.Parse("2500-11-12T01:21:00+00:00")),
new JProperty("spatialPoint", new JObject(
new JProperty("type", "Point"),
new JProperty("coordinate", new double[] { 118.9897, -46.6781 }))),
@@ -272,6 +273,73 @@ public void AllDatetimeVariationsTest()
NewtonsoftInteropTests.VerifyNewtonsoftInterop(jsonObject);
}
+ [TestMethod]
+ [Owner("mpalaparthi")]
+ public void AllDateTimeOffsetVariationsTest()
+ {
+ // Arrange
+ JObject jsonObject = new JObject();
+
+ // 1. Current UTC DateTimeOffset using JProperty
+ DateTimeOffset utcNow = DateTimeOffset.UtcNow;
+ JProperty utcNowProperty = new JProperty("currentUtcDateTimeOffset", utcNow);
+ jsonObject.Add(utcNowProperty);
+
+ // 2. Current Local DateTimeOffset using JProperty
+ DateTimeOffset localNow = DateTimeOffset.Now;
+ JProperty localNowProperty = new JProperty("currentLocalDateTimeOffset", localNow);
+ jsonObject.Add(localNowProperty);
+
+ // 3. Date Only (formatted as string) using JProperty
+ DateTimeOffset dateOnly = new DateTimeOffset(2023, 10, 26, 0, 0, 0, TimeSpan.Zero);
+ JProperty dateOnlyProperty = new JProperty("specificDateOnlyOffset", dateOnly.ToString("yyyy-MM-dd"));
+ jsonObject.Add(dateOnlyProperty);
+
+ // 4. Time Only (formatted as string) using JProperty
+ DateTimeOffset timeOnly = new DateTimeOffset(1, 1, 1, 14, 30, 0, TimeSpan.Zero);
+ JProperty timeOnlyProperty = new JProperty("specificTimeOnlyOffset", timeOnly.ToString("HH:mm:ss"));
+ jsonObject.Add(timeOnlyProperty);
+
+ // 5. DateTimeOffset with milliseconds using JProperty
+ DateTimeOffset preciseDateTimeOffset = new DateTimeOffset(2024, 5, 29, 10, 15, 30, 123, TimeSpan.FromHours(5.5));
+ JProperty preciseDateTimeOffsetProperty = new JProperty("preciseDateTimeOffset", preciseDateTimeOffset);
+ jsonObject.Add(preciseDateTimeOffsetProperty);
+
+ // 6. DateTimeOffset in ISO 8601 format (UTC) using JProperty
+ DateTimeOffset isoUtcDateTimeOffset = new DateTimeOffset(2025, 1, 15, 8, 0, 0, TimeSpan.Zero);
+ JProperty isoUtcDateTimeOffsetProperty = new JProperty("isoUtcDateTimeOffset", isoUtcDateTimeOffset.ToString("o", CultureInfo.InvariantCulture));
+ jsonObject.Add(isoUtcDateTimeOffsetProperty);
+
+ // 7. DateTimeOffset in custom format using JProperty
+ DateTimeOffset customFormattedDateTimeOffset = new DateTimeOffset(2022, 7, 1, 9, 45, 10, TimeSpan.FromHours(-4));
+ JProperty customFormattedDateTimeOffsetProperty = new JProperty("customFormattedDateTimeOffset", customFormattedDateTimeOffset.ToString("MM/dd/yyyy HH:mm:ss zzz"));
+ jsonObject.Add(customFormattedDateTimeOffsetProperty);
+
+ // 8. Nullable DateTimeOffset (representing a missing or optional date) using JProperty
+ DateTimeOffset? nullableDateTimeOffset = null;
+ JProperty nullableDateTimeOffsetProperty = new JProperty("nullableDateTimeOffset", nullableDateTimeOffset);
+ jsonObject.Add(nullableDateTimeOffsetProperty);
+
+ // 9. MinValue DateTimeOffset using JProperty
+ JProperty minDateTimeOffsetProperty = new JProperty("minDateTimeOffset", DateTimeOffset.MinValue);
+ jsonObject.Add(minDateTimeOffsetProperty);
+
+ // 10. MaxValue DateTimeOffset using JProperty
+ JProperty maxDateTimeOffsetProperty = new JProperty("maxDateTimeOffset", DateTimeOffset.MaxValue);
+ jsonObject.Add(maxDateTimeOffsetProperty);
+
+ // 11. Exact DateTimeOffset using JProperty
+ JProperty exactDateTimeOffsetProperty = new JProperty("exactDateTimeOffset", DateTimeOffset.Parse("2025-03-26T20:22:20+02:00"));
+ jsonObject.Add(exactDateTimeOffsetProperty);
+
+ // 12. Various time zone offsets
+ jsonObject.Add(new JProperty("positiveOffset", new DateTimeOffset(2025, 10, 26, 12, 0, 0, TimeSpan.FromHours(5.5))));
+ jsonObject.Add(new JProperty("negativeOffset", new DateTimeOffset(2025, 10, 26, 12, 0, 0, TimeSpan.FromHours(-8))));
+ jsonObject.Add(new JProperty("uncommonOffset", new DateTimeOffset(2025, 10, 26, 12, 0, 0, new TimeSpan(12, 45, 0))));
+
+ NewtonsoftInteropTests.VerifyNewtonsoftInterop(jsonObject);
+ }
+
public enum Day { Sun, Mon, Tue, Wed, Thu, Fri, Sat };
public sealed class ObjectWithAttributes
@@ -437,4 +505,4 @@ public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectTy
}
}
}
-}
\ No newline at end of file
+}