diff --git a/src/MongoDB.Bson/Serialization/Attributes/BsonDateOnlyOptionsAttribute.cs b/src/MongoDB.Bson/Serialization/Attributes/BsonDateOnlyOptionsAttribute.cs
new file mode 100644
index 00000000000..34ed457ddd8
--- /dev/null
+++ b/src/MongoDB.Bson/Serialization/Attributes/BsonDateOnlyOptionsAttribute.cs
@@ -0,0 +1,69 @@
+/* Copyright 2010-present MongoDB Inc.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System;
+using MongoDB.Bson.Serialization.Options;
+using MongoDB.Bson.Serialization.Serializers;
+
+namespace MongoDB.Bson.Serialization.Attributes
+{
+#if NET6_0_OR_GREATER
+ ///
+ /// Specifies the external representation and related options for a DateOnly field or property.
+ ///
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
+ public class BsonDateOnlyOptionsAttribute : BsonSerializationOptionsAttribute
+ {
+ // private fields
+ private BsonType _representation;
+ private DateOnlyDocumentFormat _documentFormat;
+
+ // constructors
+
+ ///
+ /// Initializes a new instance of the BsonDateOnlyOptionsAttribute class.
+ ///
+ /// The external representation.
+ /// The format to use with document representation.
+ public BsonDateOnlyOptionsAttribute(BsonType representation, DateOnlyDocumentFormat documentDocumentFormat = DateOnlyDocumentFormat.DateTimeTicks)
+ {
+ _representation = representation;
+ _documentFormat = documentDocumentFormat;
+ }
+
+ // public properties
+ ///
+ /// Gets the external representation.
+ ///
+ public BsonType Representation => _representation;
+
+ ///
+ /// Gets the document format.
+ ///
+ public DateOnlyDocumentFormat DocumentFormat => _documentFormat;
+
+ ///
+ /// Reconfigures the specified serializer by applying this attribute to it.
+ ///
+ /// The serializer.
+ /// A reconfigured serializer.
+ protected override IBsonSerializer Apply(IBsonSerializer serializer)
+ {
+ var reconfiguredSerializer = SerializerConfigurator.ReconfigureSerializer(serializer, (DateOnlySerializer s) => s.WithRepresentation(_representation, _documentFormat));
+ return reconfiguredSerializer ?? base.Apply(serializer);
+ }
+ }
+#endif
+}
diff --git a/src/MongoDB.Bson/Serialization/Options/DateOnlyDocumentFormat.cs b/src/MongoDB.Bson/Serialization/Options/DateOnlyDocumentFormat.cs
new file mode 100644
index 00000000000..525635d1574
--- /dev/null
+++ b/src/MongoDB.Bson/Serialization/Options/DateOnlyDocumentFormat.cs
@@ -0,0 +1,33 @@
+/* Copyright 2010-present MongoDB Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace MongoDB.Bson.Serialization.Options
+{
+ ///
+ /// Represents the format to use with a DateOnly serializer when the representation is BsonType.Document.
+ ///
+ public enum DateOnlyDocumentFormat
+ {
+ ///
+ /// The document will contain "DateTime" (BsonType.DateTime) and "Ticks" (BsonType.Int64).
+ ///
+ DateTimeTicks,
+
+ ///
+ /// The document will contain "Year", "Month" and "Day" (all BsonType.Int32).
+ ///
+ YearMonthDay
+ }
+}
\ No newline at end of file
diff --git a/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs b/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs
new file mode 100644
index 00000000000..7e1cfbecbe5
--- /dev/null
+++ b/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs
@@ -0,0 +1,50 @@
+/* Copyright 2010-present MongoDB Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+
+namespace MongoDB.Bson.Serialization
+{
+ internal static class SerializerConfigurator
+ {
+ ///
+ /// Reconfigures a serializer using the specified method.
+ /// If the serializer implements ,
+ /// the method traverses and applies the reconfiguration to its child serializers recursively until an appropriate leaf serializer is found.
+ ///
+ /// The input serializer to be reconfigured.
+ /// A function that defines how the serializer of type should be reconfigured.
+ /// The input type for the reconfigure method.
+ ///
+ /// The reconfigured serializer, or null if no leaf serializer could be reconfigured.
+ ///
+ internal static IBsonSerializer ReconfigureSerializer(IBsonSerializer serializer, Func reconfigure)
+ {
+ switch (serializer)
+ {
+ case IChildSerializerConfigurable childSerializerConfigurable:
+ var childSerializer = childSerializerConfigurable.ChildSerializer;
+ var reconfiguredChildSerializer = ReconfigureSerializer(childSerializer, reconfigure);
+ return reconfiguredChildSerializer != null? childSerializerConfigurable.WithChildSerializer(reconfiguredChildSerializer) : null;
+
+ case TSerializer typedSerializer:
+ return reconfigure(typedSerializer);
+
+ default:
+ return null;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/MongoDB.Bson/Serialization/Serializers/DateOnlySerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/DateOnlySerializer.cs
index 85bffdf7e33..6dceaf65831 100644
--- a/src/MongoDB.Bson/Serialization/Serializers/DateOnlySerializer.cs
+++ b/src/MongoDB.Bson/Serialization/Serializers/DateOnlySerializer.cs
@@ -15,6 +15,7 @@
using System;
using MongoDB.Bson.IO;
+using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson.Serialization.Options;
namespace MongoDB.Bson.Serialization.Serializers
@@ -26,7 +27,7 @@ namespace MongoDB.Bson.Serialization.Serializers
public sealed class DateOnlySerializer : StructSerializerBase, IRepresentationConfigurable
{
// static
- private static readonly DateOnlySerializer __instance = new DateOnlySerializer();
+ private static readonly DateOnlySerializer __instance = new();
///
/// Gets the default DateOnlySerializer.
@@ -38,19 +39,26 @@ private static class Flags
{
public const long DateTime = 1;
public const long Ticks = 2;
+ public const long Year = 4;
+ public const long Month = 8;
+ public const long Day = 16;
+
+ public const long DateTimeTicks = DateTime | Ticks;
+ public const long YearMonthDay = Year | Month | Day;
}
// private fields
private readonly RepresentationConverter _converter;
private readonly SerializerHelper _helper;
private readonly BsonType _representation;
+ private readonly DateOnlyDocumentFormat _documentFormat;
// constructors
///
/// Initializes a new instance of the class.
///
public DateOnlySerializer()
- : this(BsonType.DateTime)
+ : this(BsonType.DateTime, DateOnlyDocumentFormat.DateTimeTicks)
{
}
@@ -59,6 +67,16 @@ public DateOnlySerializer()
///
/// The representation.
public DateOnlySerializer(BsonType representation)
+ : this(representation, DateOnlyDocumentFormat.DateTimeTicks)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The representation.
+ /// The format to use with the BsonType.Document representation. It will be ignored if the representation is different.
+ public DateOnlySerializer(BsonType representation, DateOnlyDocumentFormat documentFormat)
{
switch (representation)
{
@@ -73,12 +91,15 @@ public DateOnlySerializer(BsonType representation)
}
_representation = representation;
+ _documentFormat = documentFormat;
_converter = new RepresentationConverter(false, false);
-
_helper = new SerializerHelper
(
- new SerializerHelper.Member("DateTime", Flags.DateTime),
- new SerializerHelper.Member("Ticks", Flags.Ticks)
+ new SerializerHelper.Member("DateTime", Flags.DateTime, isOptional: true),
+ new SerializerHelper.Member("Ticks", Flags.Ticks, isOptional: true),
+ new SerializerHelper.Member("Year", Flags.Year, isOptional: true),
+ new SerializerHelper.Member("Month", Flags.Month, isOptional: true),
+ new SerializerHelper.Member("Day", Flags.Day, isOptional: true)
);
}
@@ -86,60 +107,68 @@ public DateOnlySerializer(BsonType representation)
///
public BsonType Representation => _representation;
+ ///
+ /// The format to use for the BsonType.Document representation. It will be ignored if the representation is different.
+ ///
+ public DateOnlyDocumentFormat DocumentFormat => _documentFormat;
+
//public methods
///
public override DateOnly Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var bsonReader = context.Reader;
- DateOnly value;
var bsonType = bsonReader.GetCurrentBsonType();
switch (bsonType)
{
case BsonType.DateTime:
- value = VerifyAndMakeDateOnly(BsonUtils.ToDateTimeFromMillisecondsSinceEpoch(bsonReader.ReadDateTime()));
- break;
+ return VerifyAndMakeDateOnly(BsonUtils.ToDateTimeFromMillisecondsSinceEpoch(bsonReader.ReadDateTime()));
case BsonType.Document:
- value = default;
- _helper.DeserializeMembers(context, (_, flag) =>
+ var ticks = 0L;
+ var year = 0;
+ var month = 0;
+ var day = 0;
+
+ var foundMemberFlags = _helper.DeserializeMembers(context, (_, flag) =>
{
switch (flag)
{
- case Flags.DateTime: bsonReader.SkipValue(); break; // ignore value (use Ticks instead)
- case Flags.Ticks:
- value = VerifyAndMakeDateOnly(new DateTime(Int64Serializer.Instance.Deserialize(context), DateTimeKind.Utc));
- break;
+ case Flags.DateTime: bsonReader.SkipValue(); break; // ignore value (use Ticks instead)
+ case Flags.Ticks: ticks = Int64Serializer.Instance.Deserialize(context); break;
+ case Flags.Year: year = Int32Serializer.Instance.Deserialize(context); break;
+ case Flags.Month: month = Int32Serializer.Instance.Deserialize(context); break;
+ case Flags.Day: day = Int32Serializer.Instance.Deserialize(context); break;
}
});
- break;
+
+ return foundMemberFlags switch
+ {
+ Flags.DateTimeTicks => VerifyAndMakeDateOnly(new DateTime(ticks, DateTimeKind.Utc)),
+ Flags.YearMonthDay => new DateOnly(year, month, day),
+ _ => throw new FormatException("Invalid document format.")
+ };
case BsonType.Decimal128:
- value = VerifyAndMakeDateOnly(new DateTime(_converter.ToInt64(bsonReader.ReadDecimal128()), DateTimeKind.Utc));
- break;
+ return VerifyAndMakeDateOnly(new DateTime(_converter.ToInt64(bsonReader.ReadDecimal128()), DateTimeKind.Utc));
case BsonType.Double:
- value = VerifyAndMakeDateOnly(new DateTime(_converter.ToInt64(bsonReader.ReadDouble()), DateTimeKind.Utc));
- break;
+ return VerifyAndMakeDateOnly(new DateTime(_converter.ToInt64(bsonReader.ReadDouble()), DateTimeKind.Utc));
case BsonType.Int32:
- value = VerifyAndMakeDateOnly(new DateTime(bsonReader.ReadInt32(), DateTimeKind.Utc));
- break;
+ return VerifyAndMakeDateOnly(new DateTime(bsonReader.ReadInt32(), DateTimeKind.Utc));
case BsonType.Int64:
- value = VerifyAndMakeDateOnly(new DateTime(bsonReader.ReadInt64(), DateTimeKind.Utc));
- break;
+ return VerifyAndMakeDateOnly(new DateTime(bsonReader.ReadInt64(), DateTimeKind.Utc));
case BsonType.String:
- value = DateOnly.ParseExact(bsonReader.ReadString(), "yyyy-MM-dd");
- break;
+ return DateOnly.ParseExact(bsonReader.ReadString(), "yyyy-MM-dd");
default:
throw CreateCannotDeserializeFromBsonTypeException(bsonType);
}
- return value;
DateOnly VerifyAndMakeDateOnly(DateTime dt)
{
@@ -160,7 +189,8 @@ public override bool Equals(object obj)
return
base.Equals(obj) &&
obj is DateOnlySerializer other &&
- _representation.Equals(other._representation);
+ _representation.Equals(other._representation) &&
+ _documentFormat.Equals(other._documentFormat);
}
///
@@ -182,8 +212,17 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati
case BsonType.Document:
bsonWriter.WriteStartDocument();
- bsonWriter.WriteDateTime("DateTime", millisecondsSinceEpoch);
- bsonWriter.WriteInt64("Ticks", utcDateTime.Ticks);
+ if (_documentFormat is DateOnlyDocumentFormat.DateTimeTicks)
+ {
+ bsonWriter.WriteDateTime("DateTime", millisecondsSinceEpoch);
+ bsonWriter.WriteInt64("Ticks", utcDateTime.Ticks);
+ }
+ else
+ {
+ bsonWriter.WriteInt32("Year", value.Year);
+ bsonWriter.WriteInt32("Month", value.Month);
+ bsonWriter.WriteInt32("Day", value.Day);
+ }
bsonWriter.WriteEndDocument();
break;
@@ -200,10 +239,28 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati
}
}
+ ///
+ /// Returns a serializer that has been reconfigured with the specified representation and document format.
+ ///
+ /// The representation.
+ /// The document format to use with BsonType.Document representation.
+ ///
+ /// The reconfigured serializer.
+ ///
+ public DateOnlySerializer WithRepresentation(BsonType representation, DateOnlyDocumentFormat documentFormat)
+ {
+ if (representation == _representation && documentFormat == _documentFormat)
+ {
+ return this;
+ }
+
+ return new DateOnlySerializer(representation, documentFormat);
+ }
+
///
public DateOnlySerializer WithRepresentation(BsonType representation)
{
- return representation == _representation ? this : new DateOnlySerializer(representation);
+ return representation == _representation ? this : new DateOnlySerializer(representation, _documentFormat);
}
// explicit interface implementations
diff --git a/src/MongoDB.Bson/Serialization/Serializers/SerializerHelper.cs b/src/MongoDB.Bson/Serialization/Serializers/SerializerHelper.cs
index f9e9a6ccd79..81b43b0ce70 100644
--- a/src/MongoDB.Bson/Serialization/Serializers/SerializerHelper.cs
+++ b/src/MongoDB.Bson/Serialization/Serializers/SerializerHelper.cs
@@ -25,7 +25,6 @@ namespace MongoDB.Bson.Serialization.Serializers
public class SerializerHelper
{
// private fields
- private readonly long _allMemberFlags;
private readonly long _extraMemberFlag;
private readonly Member[] _members;
private readonly long _requiredMemberFlags;
@@ -52,7 +51,6 @@ public SerializerHelper(params Member[] members)
foreach (var member in members)
{
- _allMemberFlags |= member.Flag;
if (!member.IsOptional)
{
_requiredMemberFlags |= member.Flag;
diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/DateOnlySerializerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/DateOnlySerializerTests.cs
index b391a99fcd4..76b1159261c 100644
--- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/DateOnlySerializerTests.cs
+++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/DateOnlySerializerTests.cs
@@ -19,6 +19,8 @@
using FluentAssertions;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization;
+using MongoDB.Bson.Serialization.Attributes;
+using MongoDB.Bson.Serialization.Options;
using MongoDB.Bson.Serialization.Serializers;
using MongoDB.TestHelpers.XunitExtensions;
using Xunit;
@@ -28,6 +30,26 @@ namespace MongoDB.Bson.Tests.Serialization.Serializers
#if NET6_0_OR_GREATER
public class DateOnlySerializerTests
{
+ [Fact]
+ public void Attribute_should_set_correct_format()
+ {
+ var dateOnly = new DateOnly(2024, 10, 05);
+
+ var testObj = new TestClass
+ {
+ DateTimeTicksFormat = dateOnly,
+ YearMonthDayFormat = dateOnly,
+ IgnoredFormat = dateOnly,
+ ArrayYearMonthDayFormat = [dateOnly, dateOnly]
+ };
+
+ var json = testObj.ToJson();
+ const string expected = """
+ { "DateTimeTicksFormat" : { "DateTime" : { "$date" : "2024-10-05T00:00:00Z" }, "Ticks" : 638636832000000000 }, "YearMonthDayFormat" : { "Year" : 2024, "Month" : 10, "Day" : 5 }, "IgnoredFormat" : 638636832000000000, "ArrayYearMonthDayFormat" : [{ "Year" : 2024, "Month" : 10, "Day" : 5 }, { "Year" : 2024, "Month" : 10, "Day" : 5 }] }
+ """;
+ Assert.Equal(expected, json);
+ }
+
[Fact]
public void Constructor_with_no_arguments_should_return_expected_result()
{
@@ -47,6 +69,20 @@ public void Constructor_with_representation_should_return_expected_result(
subject.Representation.Should().Be(representation);
}
+ [Theory]
+ [ParameterAttributeData]
+ public void Constructor_with_representation_and_format_should_return_expected_result(
+ [Values(BsonType.DateTime, BsonType.String, BsonType.Int64, BsonType.Document)]
+ BsonType representation,
+ [Values(DateOnlyDocumentFormat.DateTimeTicks, DateOnlyDocumentFormat.YearMonthDay)]
+ DateOnlyDocumentFormat format)
+ {
+ var subject = new DateOnlySerializer(representation, format);
+
+ subject.Representation.Should().Be(representation);
+ subject.DocumentFormat.Should().Be(format);
+ }
+
[Theory]
[InlineData("""{ "x" : { "$numberDouble" : "638649792000000000" } }""","2024-10-20" )]
[InlineData("""{ "x" : { "$numberDecimal" : "638649792000000000" } }""","2024-10-20" )]
@@ -81,6 +117,9 @@ public void Deserialize_should_be_forgiving_of_actual_numeric_types(string json,
[InlineData("""{ "x" : { "DateTime" : { "$date" : { "$numberLong" : "1729382400000" } }, "Ticks" : { "$numberLong" : "638649792000000000" } } }""","2024-10-20" )]
[InlineData("""{ "x" : { "DateTime" : { "$date" : { "$numberLong" : "-62135596800000" } }, "Ticks" : { "$numberLong" : "0" } } }""","0001-01-01" )]
[InlineData("""{ "x" : { "DateTime" : { "$date" : { "$numberLong" : "253402214400000" } }, "Ticks" : { "$numberLong" : "3155378112000000000" } } }""","9999-12-31" )]
+ [InlineData("""{ "x" : { "Year" : 2024, "Month" : 10, "Day" : 5 } }""","2024-10-05" )]
+ [InlineData("""{ "x" : { "Year" : 9999, "Month" : 12, "Day" : 31 } }""","9999-12-31" )]
+ [InlineData("""{ "x" : { "Year" : 1, "Month" : 1, "Day" : 1 } }""","0001-01-01" )]
public void Deserialize_should_have_expected_result(string json, string expectedResult)
{
var subject = new DateOnlySerializer();
@@ -95,6 +134,27 @@ public void Deserialize_should_have_expected_result(string json, string expected
result.Should().Be(DateOnly.Parse(expectedResult, CultureInfo.InvariantCulture));
}
+ [Theory]
+ [InlineData("""{ "x" : { "Year" : 2024, "Month" : 10, "Day" : 5 } }""","2024-10-05" )]
+ [InlineData("""{ "x" : { "Year" : 9999, "Month" : 12, "Day" : 31 } }""","9999-12-31" )]
+ [InlineData("""{ "x" : { "Year" : 1, "Month" : 1, "Day" : 1 } }""","0001-01-01" )]
+ [InlineData("""{ "x" : { "DateTime" : { "$date" : { "$numberLong" : "1729382400000" } }, "Ticks" : { "$numberLong" : "638649792000000000" } } }""","2024-10-20" )]
+ [InlineData("""{ "x" : { "DateTime" : { "$date" : { "$numberLong" : "-62135596800000" } }, "Ticks" : { "$numberLong" : "0" } } }""","0001-01-01" )]
+ [InlineData("""{ "x" : { "DateTime" : { "$date" : { "$numberLong" : "253402214400000" } }, "Ticks" : { "$numberLong" : "3155378112000000000" } } }""","9999-12-31" )]
+ public void Deserialize_with_human_readable_should_have_expected_result(string json, string expectedResult)
+ {
+ var subject = new DateOnlySerializer(BsonType.Document, DateOnlyDocumentFormat.YearMonthDay);
+
+ using var reader = new JsonReader(json);
+ reader.ReadStartDocument();
+ reader.ReadName("x");
+ var context = BsonDeserializationContext.CreateRoot(reader);
+ var result = subject.Deserialize(context);
+ reader.ReadEndDocument();
+
+ result.Should().Be(DateOnly.Parse(expectedResult, CultureInfo.InvariantCulture));
+ }
+
[Theory]
[InlineData("""{ "x" : { "$date" : { "$numberLong" : "1729382410000" } } }""")]
[InlineData("""{ "x" : { "$numberLong" : "638649792100000000" } }""")]
@@ -113,6 +173,22 @@ public void Deserialize_should_throw_when_date_has_time(string json)
exception.Message.Should().Be("Deserialized value has a non-zero time component.");
}
+ [Theory]
+ [InlineData("""{ "x" : { "Year": 2024, "DateTime" : { "$date" : { "$numberLong" : "1729382400000" } }, "Ticks" : { "$numberLong" : "638649792100000000" } } }""")]
+ public void Deserialize_should_throw_when_document_format_is_invalid(string json)
+ {
+ var subject = new DateOnlySerializer();
+
+ 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 document format.");
+ }
+
[Fact]
public void Equals_null_should_return_false()
{
@@ -156,17 +232,28 @@ public void Equals_with_equal_fields_should_return_true()
}
[Theory]
- [InlineData(BsonType.String)]
- [InlineData(BsonType.Int64)]
- [InlineData(BsonType.Document)]
- public void Equals_with_not_equal_fields_should_return_true(BsonType representation)
+ [ParameterAttributeData]
+ public void Equals_with_different_representation_and_format_should_return_correct(
+ [Values(BsonType.DateTime, BsonType.String, BsonType.Int64, BsonType.Document)]
+ BsonType representation,
+ [Values(DateOnlyDocumentFormat.DateTimeTicks, DateOnlyDocumentFormat.YearMonthDay)]
+ DateOnlyDocumentFormat format)
{
var x = new DateOnlySerializer();
- var y = new DateOnlySerializer(representation);
+ var y = new DateOnlySerializer(representation, format);
var result = x.Equals(y);
+ var result2 = y.Equals(x);
+ result.Should().Be(result2);
- result.Should().Be(false);
+ if (representation == BsonType.DateTime && format == DateOnlyDocumentFormat.DateTimeTicks)
+ {
+ result.Should().Be(true);
+ }
+ else
+ {
+ result.Should().Be(false);
+ }
}
[Fact]
@@ -220,6 +307,29 @@ public void Serialize_should_have_expected_result(BsonType representation, strin
result.Should().Be(expectedResult);
}
+ [Theory]
+ [InlineData("2024-10-20", """{ "x" : { "Year" : { "$numberInt" : "2024" }, "Month" : { "$numberInt" : "10" }, "Day" : { "$numberInt" : "20" } } }""")]
+ [InlineData( "0001-01-01", """{ "x" : { "Year" : { "$numberInt" : "1" }, "Month" : { "$numberInt" : "1" }, "Day" : { "$numberInt" : "1" } } }""")]
+ [InlineData("9999-12-31", """{ "x" : { "Year" : { "$numberInt" : "9999" }, "Month" : { "$numberInt" : "12" }, "Day" : { "$numberInt" : "31" } } }""")]
+ public void Serialize_human_readable_should_have_expected_result(string valueString, string expectedResult)
+ {
+ var subject = new DateOnlySerializer(BsonType.Document, DateOnlyDocumentFormat.YearMonthDay);
+ var value = DateOnly.Parse(valueString, CultureInfo.InvariantCulture);
+
+ using var textWriter = new StringWriter();
+ using var writer = new JsonWriter(textWriter,
+ new JsonWriterSettings { OutputMode = JsonOutputMode.CanonicalExtendedJson });
+
+ var context = BsonSerializationContext.CreateRoot(writer);
+ writer.WriteStartDocument();
+ writer.WriteName("x");
+ subject.Serialize(context, value);
+ writer.WriteEndDocument();
+ var result = textWriter.ToString();
+
+ result.Should().Be(expectedResult);
+ }
+
[Fact]
public void Serializer_should_be_registered()
{
@@ -244,6 +354,21 @@ public void WithRepresentation_should_return_expected_result(
result.Should().BeSameAs(subject);
}
}
+
+ private class TestClass
+ {
+ [BsonDateOnlyOptions(BsonType.Document, DateOnlyDocumentFormat.DateTimeTicks)]
+ public DateOnly DateTimeTicksFormat { get; set; }
+
+ [BsonDateOnlyOptions(BsonType.Document, DateOnlyDocumentFormat.YearMonthDay)]
+ public DateOnly YearMonthDayFormat { get; set; }
+
+ [BsonDateOnlyOptions(BsonType.Int64, DateOnlyDocumentFormat.YearMonthDay)]
+ public DateOnly IgnoredFormat { get; set; }
+
+ [BsonDateOnlyOptions(BsonType.Document, DateOnlyDocumentFormat.YearMonthDay)]
+ public DateOnly[] ArrayYearMonthDayFormat { get; set; }
+ }
}
#endif
}
\ No newline at end of file