From b7f6663076918861d2bf11c650ac51767ec390e4 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 30 Sep 2025 18:32:18 +0200 Subject: [PATCH 1/5] CSHARP-5737: Add legacy representation for TimeOnly --- .../Serializers/DateOnlySerializer.cs | 1 - .../Serializers/TimeOnlySerializer.cs | 66 ++++++++++++++++- .../Serializers/TimeOnlySerializerTests.cs | 74 ++++++++++++++++--- 3 files changed, 129 insertions(+), 12 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/Serializers/DateOnlySerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/DateOnlySerializer.cs index 6dceaf65831..bb571238b86 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/DateOnlySerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/DateOnlySerializer.cs @@ -15,7 +15,6 @@ using System; using MongoDB.Bson.IO; -using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Options; namespace MongoDB.Bson.Serialization.Serializers diff --git a/src/MongoDB.Bson/Serialization/Serializers/TimeOnlySerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/TimeOnlySerializer.cs index 8b765217005..93d5b3aa2f6 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/TimeOnlySerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/TimeOnlySerializer.cs @@ -14,6 +14,7 @@ */ using System; +using MongoDB.Bson.IO; using MongoDB.Bson.Serialization.Options; namespace MongoDB.Bson.Serialization.Serializers @@ -32,7 +33,20 @@ public sealed class TimeOnlySerializer: StructSerializerBase, IReprese /// public static TimeOnlySerializer Instance => __instance; + // private constants + private static class Flags + { + public const long Hour = 1; + public const long Minute = 2; + public const long Second = 4; + public const long Millisecond = 8; + public const long Microsecond = 16; + public const long Nanosecond = 32; + public const long Ticks = 64; + } + // private fields + private readonly SerializerHelper _helper; private readonly BsonType _representation; private readonly TimeOnlyUnits _units; @@ -58,7 +72,7 @@ public TimeOnlySerializer(BsonType representation) /// Initializes a new instance of the class. /// /// The representation. - /// The units. + /// The units. Ignored if representation is BsonType.Document. public TimeOnlySerializer(BsonType representation, TimeOnlyUnits units) { switch (representation) @@ -67,6 +81,7 @@ public TimeOnlySerializer(BsonType representation, TimeOnlyUnits units) case BsonType.Int32: case BsonType.Int64: case BsonType.String: + case BsonType.Document: break; default: @@ -75,6 +90,17 @@ public TimeOnlySerializer(BsonType representation, TimeOnlyUnits units) _representation = representation; _units = units; + + _helper = new SerializerHelper + ( + new SerializerHelper.Member("Hour", Flags.Hour, isOptional: true), + new SerializerHelper.Member("Minute", Flags.Minute, isOptional: true), + new SerializerHelper.Member("Second", Flags.Second, isOptional: true), + new SerializerHelper.Member("Millisecond", Flags.Millisecond, isOptional: true), + new SerializerHelper.Member("Microsecond", Flags.Microsecond, isOptional: true), + new SerializerHelper.Member("Nanosecond", Flags.Nanosecond, isOptional: true), + new SerializerHelper.Member("Ticks", Flags.Ticks, isOptional: false) + ); } // public properties @@ -102,6 +128,7 @@ public override TimeOnly Deserialize(BsonDeserializationContext context, BsonDes BsonType.Int64 => FromInt64(bsonReader.ReadInt64(), _units), BsonType.Int32 => FromInt32(bsonReader.ReadInt32(), _units), BsonType.Double => FromDouble(bsonReader.ReadDouble(), _units), + BsonType.Document => FromDocument(context), _ => throw CreateCannotDeserializeFromBsonTypeException(bsonType) }; } @@ -145,6 +172,20 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati bsonWriter.WriteString(value.ToString("o")); break; + case BsonType.Document: + bsonWriter.WriteStartDocument(); + bsonWriter.WriteInt32("Hour", value.Hour); + bsonWriter.WriteInt32("Minute", value.Minute); + bsonWriter.WriteInt32("Second", value.Second); + bsonWriter.WriteInt32("Millisecond", value.Millisecond); +#if NET7_0_OR_GREATER + bsonWriter.WriteInt32("Microsecond", value.Microsecond); + bsonWriter.WriteInt32("Nanosecond", value.Nanosecond); +#endif + bsonWriter.WriteInt64("Ticks", value.Ticks); + bsonWriter.WriteEndDocument(); + break; + default: throw new BsonSerializationException($"'{_representation}' is not a valid TimeOnly representation."); } @@ -196,6 +237,29 @@ private TimeOnly FromInt64(long value, TimeOnlyUnits units) : new TimeOnly(value * TicksPerUnit(units)); } + private TimeOnly FromDocument(BsonDeserializationContext context) + { + var bsonReader = context.Reader; + var ticks = 0L; + + _helper.DeserializeMembers(context, (_, flag) => + { + switch (flag) + { + case Flags.Hour: + case Flags.Minute: + case Flags.Second: + case Flags.Millisecond: + case Flags.Microsecond: + case Flags.Nanosecond: + bsonReader.SkipValue(); break; // ignore value (use Ticks instead) + case Flags.Ticks: ticks = Int64Serializer.Instance.Deserialize(context); break; + } + }); + + return FromInt64(ticks, TimeOnlyUnits.Ticks); + } + private long TicksPerUnit(TimeOnlyUnits units) { return units switch diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/TimeOnlySerializerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/TimeOnlySerializerTests.cs index ea344347f56..63531982baa 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/TimeOnlySerializerTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/TimeOnlySerializerTests.cs @@ -43,17 +43,21 @@ public void Attribute_should_set_correct_units() Microseconds = timeOnly, Ticks = timeOnly, Nanoseconds = timeOnly, + Document = timeOnly }; var json = testObj.ToJson(); - var expected = "{ \"Hours\" : 13, " - + "\"Minutes\" : 804, " - + "\"Seconds\" : 48293, " - + "\"Milliseconds\" : 48293000, " - + "\"Microseconds\" : 48293000000, " - + "\"Ticks\" : 482930000000, " - + "\"Nanoseconds\" : 48293000000000 }"; + var baseString = """ + { "Hours" : 13, "Minutes" : 804, "Seconds" : 48293, "Milliseconds" : 48293000, "Microseconds" : 48293000000, "Ticks" : 482930000000, "Nanoseconds" : 48293000000000 + """; + + var documentString = """ + { "Hour" : 13, "Minute" : 24, "Second" : 53, "Millisecond" : 0, "Ticks" : 482930000000 } + """; + + + var expected = baseString + """, "Document" : """ + documentString + " }"; Assert.Equal(expected, json); } @@ -69,7 +73,7 @@ public void Constructor_with_no_arguments_should_return_expected_result() [Theory] [ParameterAttributeData] public void Constructor_with_representation_should_return_expected_result( - [Values(BsonType.String, BsonType.Int64, BsonType.Int32, BsonType.Double)] + [Values(BsonType.String, BsonType.Int64, BsonType.Int32, BsonType.Double, BsonType.Document)] BsonType representation, [Values(TimeOnlyUnits.Ticks, TimeOnlyUnits.Hours, TimeOnlyUnits.Minutes, TimeOnlyUnits.Seconds, TimeOnlyUnits.Milliseconds, TimeOnlyUnits.Microseconds, TimeOnlyUnits.Nanoseconds)] @@ -81,6 +85,53 @@ public void Constructor_with_representation_should_return_expected_result( subject.Units.Should().Be(units); } + [Theory] + [InlineData("""{ "x" : { Ticks: { "$numberLong" : "307255946583" } } }""","08:32:05.5946583" )] + [InlineData("""{ "x" : { Ticks: { "$numberLong" : "0" } } }""","00:00:00.0000000" )] + [InlineData("""{ "x" : { Ticks: { "$numberLong" : "863999999999" } } }""","23:59:59.9999999" )] + public void Deserialize_with_document_should_have_expected_result(string json, string expectedResult) + { + var subject = new TimeOnlySerializer(); + TestDeserialize(subject, json, expectedResult); + } + + [Theory] + [InlineData("""{ "x" : { Ticks: { "$numberDouble" : "307255946583" } } }""","08:32:05.5946583" )] + [InlineData("""{ "x" : { Ticks: { "$numberDecimal" : "307255946583" } } }""","08:32:05.5946583" )] + public void Deserialize_with_document_should_be_forgiving_of_actual_numeric_type(string json, string expectedResult) + { + var subject = new TimeOnlySerializer(); + TestDeserialize(subject, json, expectedResult); + } + + [Theory] + [InlineData(""" + { "x" : { Hour: { "$numberInt": 0 }, Minute: { "$numberInt": 0 }, Second: { "$numberInt": 0 }, + Millisecond: { "$numberInt": 0 }, Microsecond: { "$numberInt": 0 }, Nanosecond: { "$numberInt": 0 }, + Ticks: { "$numberDouble" : "307255946583" } } } + ""","08:32:05.5946583" )] + public void Deserialize_with_document_should_ignore_other_time_components(string json, string expectedResult) + { + var subject = new TimeOnlySerializer(); + TestDeserialize(subject, json, expectedResult); + } + + [Theory] + [InlineData("""{ "x" : { "Unknown": "test", Ticks: { "$numberDouble" : "307255946583" } } }""" )] + public void Deserialize_with_document_should_throw_when_field_is_unknown(string json) + { + var subject = new TimeOnlySerializer(); + + using var reader = new JsonReader(json); + reader.ReadStartDocument(); + reader.ReadName("x"); + var context = BsonDeserializationContext.CreateRoot(reader); + + var exception = Record.Exception(() => subject.Deserialize(context)); + exception.Should().BeOfType(); + exception.Message.Should().Be("Invalid element: 'Unknown'."); + } + [Theory] [InlineData("""{ "x" : "08:32:05.5946583" }""","08:32:05.5946583" )] [InlineData("""{ "x" : "00:00:00.0000000" }""","00:00:00.0000000")] @@ -407,8 +458,8 @@ public void Serializer_should_be_registered() [Theory] [ParameterAttributeData] public void WithRepresentation_should_return_expected_result( - [Values(BsonType.String, BsonType.Int64, BsonType.Int32, BsonType.Double)] BsonType oldRepresentation, - [Values(BsonType.String, BsonType.Int64, BsonType.Int32, BsonType.Double)] BsonType newRepresentation) + [Values(BsonType.String, BsonType.Int64, BsonType.Int32, BsonType.Double, BsonType.Document)] BsonType oldRepresentation, + [Values(BsonType.String, BsonType.Int64, BsonType.Int32, BsonType.Double, BsonType.Document)] BsonType newRepresentation) { var subject = new TimeOnlySerializer(oldRepresentation); @@ -473,6 +524,9 @@ private class TestClass [BsonTimeOnlyOptions(BsonType.Int64, TimeOnlyUnits.Nanoseconds )] public TimeOnly Nanoseconds { get; set; } + + [BsonTimeOnlyOptions(BsonType.Document, TimeOnlyUnits.Nanoseconds )] + public TimeOnly Document { get; set; } } } #endif From f3076bf79823072924db4108c52a4912f5a7b924 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 30 Sep 2025 18:54:09 +0200 Subject: [PATCH 2/5] Added serialization test --- .../Serializers/TimeOnlySerializerTests.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/TimeOnlySerializerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/TimeOnlySerializerTests.cs index 63531982baa..5dd5d360bc7 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/TimeOnlySerializerTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/TimeOnlySerializerTests.cs @@ -324,6 +324,17 @@ public void GetHashCode_should_return_zero() result.Should().Be(0); } + [Theory] + [InlineData("08:32:05.5946583", """{ "x" : { "Hour" : { "$numberInt" : "8" }, "Minute" : { "$numberInt" : "32" }, "Second" : { "$numberInt" : "5" }, "Millisecond" : { "$numberInt" : "594" }, "Ticks" : { "$numberLong" : "307255946583" } } }""")] + [InlineData("00:00:00.0000000", """{ "x" : { "Hour" : { "$numberInt" : "0" }, "Minute" : { "$numberInt" : "0" }, "Second" : { "$numberInt" : "0" }, "Millisecond" : { "$numberInt" : "0" }, "Ticks" : { "$numberLong" : "0" } } }""")] + [InlineData("23:59:59.9999999", """{ "x" : { "Hour" : { "$numberInt" : "23" }, "Minute" : { "$numberInt" : "59" }, "Second" : { "$numberInt" : "59" }, "Millisecond" : { "$numberInt" : "999" }, "Ticks" : { "$numberLong" : "863999999999" } } }""")] + public void Serialize_with_document_representation_should_have_expected_result(string valueString, string expectedResult) + { + var subject = new TimeOnlySerializer(BsonType.Document); + + TestSerialize(subject, valueString, expectedResult); + } + [Theory] [InlineData(BsonType.String, "08:32:05.5946583", """{ "x" : "08:32:05.5946583" }""")] [InlineData(BsonType.String, "00:00:00.0000000", """{ "x" : "00:00:00.0000000" }""")] @@ -525,7 +536,7 @@ private class TestClass [BsonTimeOnlyOptions(BsonType.Int64, TimeOnlyUnits.Nanoseconds )] public TimeOnly Nanoseconds { get; set; } - [BsonTimeOnlyOptions(BsonType.Document, TimeOnlyUnits.Nanoseconds )] + [BsonTimeOnlyOptions(BsonType.Document)] public TimeOnly Document { get; set; } } } From e184088982879eff2e99ab78fe512f46e7a97fa0 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 30 Sep 2025 19:04:07 +0200 Subject: [PATCH 3/5] added comment --- .../Serialization/Attributes/BsonTimeOnlyOptionsAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MongoDB.Bson/Serialization/Attributes/BsonTimeOnlyOptionsAttribute.cs b/src/MongoDB.Bson/Serialization/Attributes/BsonTimeOnlyOptionsAttribute.cs index 23f327f3290..17457ae3fd6 100644 --- a/src/MongoDB.Bson/Serialization/Attributes/BsonTimeOnlyOptionsAttribute.cs +++ b/src/MongoDB.Bson/Serialization/Attributes/BsonTimeOnlyOptionsAttribute.cs @@ -44,7 +44,7 @@ public BsonTimeOnlyOptionsAttribute(BsonType representation) /// Initializes a new instance of the BsonTimeOnlyOptionsAttribute class. /// /// The external representation. - /// The TimeOnlyUnits. + /// The TimeOnlyUnits. Ignored if representation is BsonsType.Document. public BsonTimeOnlyOptionsAttribute(BsonType representation, TimeOnlyUnits units) { _representation = representation; From 9ef4586e881e63aad35f61a8329c338a81e24c83 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 1 Oct 2025 17:00:40 +0200 Subject: [PATCH 4/5] Added micro and nanosecond support --- .../Serializers/TimeOnlySerializer.cs | 19 +++++++++++++++---- .../Serializers/TimeOnlySerializerTests.cs | 8 ++++---- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/Serializers/TimeOnlySerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/TimeOnlySerializer.cs index 93d5b3aa2f6..4ac895a46e6 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/TimeOnlySerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/TimeOnlySerializer.cs @@ -178,10 +178,9 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati bsonWriter.WriteInt32("Minute", value.Minute); bsonWriter.WriteInt32("Second", value.Second); bsonWriter.WriteInt32("Millisecond", value.Millisecond); -#if NET7_0_OR_GREATER - bsonWriter.WriteInt32("Microsecond", value.Microsecond); - bsonWriter.WriteInt32("Nanosecond", value.Nanosecond); -#endif + // Microsecond and Nanosecond properties were added in .NET 7 + bsonWriter.WriteInt32("Microsecond", GetMicrosecondsComponent(value.Ticks)); + bsonWriter.WriteInt32("Nanosecond", GetNanosecondsComponent(value.Ticks)); bsonWriter.WriteInt64("Ticks", value.Ticks); bsonWriter.WriteEndDocument(); break; @@ -295,6 +294,18 @@ private long ToInt64(TimeOnly timeOnly, TimeOnlyUnits units) : timeOnly.Ticks / TicksPerUnit(units); } + private int GetNanosecondsComponent(long ticks) + { + // ticks % 10 * 100 + return (int)(ticks % TicksPerUnit(TimeOnlyUnits.Microseconds) * 100); + } + + private int GetMicrosecondsComponent(long ticks) + { + // ticks / 10 % 1000 + return (int)(ticks / TicksPerUnit(TimeOnlyUnits.Microseconds) % 1000); + } + // explicit interface implementations IBsonSerializer IRepresentationConfigurable.WithRepresentation(BsonType representation) { diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/TimeOnlySerializerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/TimeOnlySerializerTests.cs index 5dd5d360bc7..54d781a27bd 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/TimeOnlySerializerTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/TimeOnlySerializerTests.cs @@ -53,7 +53,7 @@ public void Attribute_should_set_correct_units() """; var documentString = """ - { "Hour" : 13, "Minute" : 24, "Second" : 53, "Millisecond" : 0, "Ticks" : 482930000000 } + { "Hour" : 13, "Minute" : 24, "Second" : 53, "Millisecond" : 0, "Microsecond" : 0, "Nanosecond" : 0, "Ticks" : 482930000000 } """; @@ -325,9 +325,9 @@ public void GetHashCode_should_return_zero() } [Theory] - [InlineData("08:32:05.5946583", """{ "x" : { "Hour" : { "$numberInt" : "8" }, "Minute" : { "$numberInt" : "32" }, "Second" : { "$numberInt" : "5" }, "Millisecond" : { "$numberInt" : "594" }, "Ticks" : { "$numberLong" : "307255946583" } } }""")] - [InlineData("00:00:00.0000000", """{ "x" : { "Hour" : { "$numberInt" : "0" }, "Minute" : { "$numberInt" : "0" }, "Second" : { "$numberInt" : "0" }, "Millisecond" : { "$numberInt" : "0" }, "Ticks" : { "$numberLong" : "0" } } }""")] - [InlineData("23:59:59.9999999", """{ "x" : { "Hour" : { "$numberInt" : "23" }, "Minute" : { "$numberInt" : "59" }, "Second" : { "$numberInt" : "59" }, "Millisecond" : { "$numberInt" : "999" }, "Ticks" : { "$numberLong" : "863999999999" } } }""")] + [InlineData("08:32:05.5946583", """{ "x" : { "Hour" : { "$numberInt" : "8" }, "Minute" : { "$numberInt" : "32" }, "Second" : { "$numberInt" : "5" }, "Millisecond" : { "$numberInt" : "594" }, "Microsecond" : { "$numberInt" : "658" }, "Nanosecond" : { "$numberInt" : "300" }, "Ticks" : { "$numberLong" : "307255946583" } } }""")] + [InlineData("00:00:00.0000000", """{ "x" : { "Hour" : { "$numberInt" : "0" }, "Minute" : { "$numberInt" : "0" }, "Second" : { "$numberInt" : "0" }, "Millisecond" : { "$numberInt" : "0" }, "Microsecond" : { "$numberInt" : "0" }, "Nanosecond" : { "$numberInt" : "0" }, "Ticks" : { "$numberLong" : "0" } } }""")] + [InlineData("23:59:59.9999999", """{ "x" : { "Hour" : { "$numberInt" : "23" }, "Minute" : { "$numberInt" : "59" }, "Second" : { "$numberInt" : "59" }, "Millisecond" : { "$numberInt" : "999" }, "Microsecond" : { "$numberInt" : "999" }, "Nanosecond" : { "$numberInt" : "900" }, "Ticks" : { "$numberLong" : "863999999999" } } }""")] public void Serialize_with_document_representation_should_have_expected_result(string valueString, string expectedResult) { var subject = new TimeOnlySerializer(BsonType.Document); From 0f833fbb3997d3d661d112958995f2418153a898 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 2 Oct 2025 17:40:39 +0200 Subject: [PATCH 5/5] Fixed spelling and ordering --- .../BsonTimeOnlyOptionsAttribute.cs | 2 +- .../Serializers/TimeOnlySerializer.cs | 35 ++++++++++--------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/Attributes/BsonTimeOnlyOptionsAttribute.cs b/src/MongoDB.Bson/Serialization/Attributes/BsonTimeOnlyOptionsAttribute.cs index 17457ae3fd6..0593b3661db 100644 --- a/src/MongoDB.Bson/Serialization/Attributes/BsonTimeOnlyOptionsAttribute.cs +++ b/src/MongoDB.Bson/Serialization/Attributes/BsonTimeOnlyOptionsAttribute.cs @@ -44,7 +44,7 @@ public BsonTimeOnlyOptionsAttribute(BsonType representation) /// Initializes a new instance of the BsonTimeOnlyOptionsAttribute class. /// /// The external representation. - /// The TimeOnlyUnits. Ignored if representation is BsonsType.Document. + /// The TimeOnlyUnits. Ignored if representation is BsonType.Document. public BsonTimeOnlyOptionsAttribute(BsonType representation, TimeOnlyUnits units) { _representation = representation; diff --git a/src/MongoDB.Bson/Serialization/Serializers/TimeOnlySerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/TimeOnlySerializer.cs index 4ac895a46e6..cd6f0e21361 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/TimeOnlySerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/TimeOnlySerializer.cs @@ -77,11 +77,11 @@ public TimeOnlySerializer(BsonType representation, TimeOnlyUnits units) { switch (representation) { + case BsonType.Document: case BsonType.Double: case BsonType.Int32: case BsonType.Int64: case BsonType.String: - case BsonType.Document: break; default: @@ -124,11 +124,11 @@ public override TimeOnly Deserialize(BsonDeserializationContext context, BsonDes return bsonType switch { - BsonType.String => TimeOnly.ParseExact(bsonReader.ReadString(), "o"), - BsonType.Int64 => FromInt64(bsonReader.ReadInt64(), _units), - BsonType.Int32 => FromInt32(bsonReader.ReadInt32(), _units), - BsonType.Double => FromDouble(bsonReader.ReadDouble(), _units), BsonType.Document => FromDocument(context), + BsonType.Double => FromDouble(bsonReader.ReadDouble(), _units), + BsonType.Int32 => FromInt32(bsonReader.ReadInt32(), _units), + BsonType.Int64 => FromInt64(bsonReader.ReadInt64(), _units), + BsonType.String => TimeOnly.ParseExact(bsonReader.ReadString(), "o"), _ => throw CreateCannotDeserializeFromBsonTypeException(bsonType) }; } @@ -156,6 +156,19 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati switch (_representation) { + case BsonType.Document: + bsonWriter.WriteStartDocument(); + bsonWriter.WriteInt32("Hour", value.Hour); + bsonWriter.WriteInt32("Minute", value.Minute); + bsonWriter.WriteInt32("Second", value.Second); + bsonWriter.WriteInt32("Millisecond", value.Millisecond); + // Microsecond and Nanosecond properties were added in .NET 7 + bsonWriter.WriteInt32("Microsecond", GetMicrosecondsComponent(value.Ticks)); + bsonWriter.WriteInt32("Nanosecond", GetNanosecondsComponent(value.Ticks)); + bsonWriter.WriteInt64("Ticks", value.Ticks); + bsonWriter.WriteEndDocument(); + break; + case BsonType.Double: bsonWriter.WriteDouble(ToDouble(value, _units)); break; @@ -172,18 +185,6 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati bsonWriter.WriteString(value.ToString("o")); break; - case BsonType.Document: - bsonWriter.WriteStartDocument(); - bsonWriter.WriteInt32("Hour", value.Hour); - bsonWriter.WriteInt32("Minute", value.Minute); - bsonWriter.WriteInt32("Second", value.Second); - bsonWriter.WriteInt32("Millisecond", value.Millisecond); - // Microsecond and Nanosecond properties were added in .NET 7 - bsonWriter.WriteInt32("Microsecond", GetMicrosecondsComponent(value.Ticks)); - bsonWriter.WriteInt32("Nanosecond", GetNanosecondsComponent(value.Ticks)); - bsonWriter.WriteInt64("Ticks", value.Ticks); - bsonWriter.WriteEndDocument(); - break; default: throw new BsonSerializationException($"'{_representation}' is not a valid TimeOnly representation.");