diff --git a/YamlDotNet.Test/Serialization/DateOnlyConverterTests.cs b/YamlDotNet.Test/Serialization/DateOnlyConverterTests.cs
new file mode 100644
index 00000000..dbfce3b0
--- /dev/null
+++ b/YamlDotNet.Test/Serialization/DateOnlyConverterTests.cs
@@ -0,0 +1,347 @@
+// This file is part of YamlDotNet - A .NET library for YAML.
+// Copyright (c) Antoine Aubry and contributors
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do
+// so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+#if NET6_0_OR_GREATER
+using System;
+using System.Globalization;
+using FakeItEasy;
+using FluentAssertions;
+using Xunit;
+using YamlDotNet.Core;
+using YamlDotNet.Core.Events;
+using YamlDotNet.Serialization;
+using YamlDotNet.Serialization.Converters;
+using YamlDotNet.Serialization.NamingConventions;
+
+namespace YamlDotNet.Test.Serialization
+{
+ ///
+ /// This represents the test entity for the class.
+ ///
+ public class DateOnlyConverterTests
+ {
+ ///
+ /// Tests whether the Accepts() method should return expected result or not.
+ ///
+ /// to check.
+ /// Expected result.
+ [Theory]
+ [InlineData(typeof(DateOnly), true)]
+ [InlineData(typeof(string), false)]
+ public void Given_Type_Accepts_ShouldReturn_Result(Type type, bool expected)
+ {
+ var converter = new DateOnlyConverter();
+
+ var result = converter.Accepts(type);
+
+ result.Should().Be(expected);
+ }
+
+ ///
+ /// Tests whether the ReadYaml() method should throw or not.
+ ///
+ /// Year value.
+ /// Month value.
+ /// Day value.
+ /// The converter instance uses its default parameter of "d".
+ [Theory]
+ [InlineData(2016, 12, 31)]
+ public void Given_Yaml_WithInvalidDateTimeFormat_WithDefaultParameter_ReadYaml_ShouldThrow_Exception(int year, int month, int day)
+ {
+ var yaml = $"{year}-{month:00}-{day:00}";
+
+ var parser = A.Fake();
+ A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml));
+
+ var converter = new DateOnlyConverter();
+
+ Action action = () => { converter.ReadYaml(parser, typeof(DateOnly)); };
+
+ action.ShouldThrow();
+ }
+
+ ///
+ /// Tests whether the ReadYaml() method should return expected result or not.
+ ///
+ /// Year value.
+ /// Month value.
+ /// Day value.
+ /// The converter instance uses its default parameter of "d".
+ [Theory]
+ [InlineData(2016, 12, 31)]
+ public void Given_Yaml_WithValidDateTimeFormat_WithDefaultParameter_ReadYaml_ShouldReturn_Result(int year, int month, int day)
+ {
+ var yaml = $"{month:00}/{day:00}/{year}"; // This is the DateOnly format of "d"
+
+ var parser = A.Fake();
+ A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml));
+
+ var converter = new DateOnlyConverter();
+
+ var result = converter.ReadYaml(parser, typeof(DateOnly));
+
+ result.Should().BeOfType();
+ ((DateOnly)result).Year.Should().Be(year);
+ ((DateOnly)result).Month.Should().Be(month);
+ ((DateOnly)result).Day.Should().Be(day);
+ }
+
+ ///
+ /// Tests whether the ReadYaml() method should return expected result or not.
+ ///
+ /// Year value.
+ /// Month value.
+ /// Day value.
+ /// Designated date/time format 1.
+ /// Designated date/time format 2.
+ [Theory]
+ [InlineData(2016, 12, 31, "yyyy-MM-dd", "yyyy/MM/dd")]
+ public void Given_Yaml_WithValidDateTimeFormat_ReadYaml_ShouldReturn_Result(int year, int month, int day, string format1, string format2)
+ {
+ var yaml = $"{year}-{month:00}-{day:00}";
+
+ var parser = A.Fake();
+ A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml));
+
+ var converter = new DateOnlyConverter(formats: new[] { format1, format2 });
+
+ var result = converter.ReadYaml(parser, typeof(DateOnly));
+
+ result.Should().BeOfType();
+ ((DateOnly)result).Year.Should().Be(year);
+ ((DateOnly)result).Month.Should().Be(month);
+ ((DateOnly)result).Day.Should().Be(day);
+ }
+
+ ///
+ /// Tests whether the ReadYaml() method should return expected result or not.
+ ///
+ /// Year value.
+ /// Month value.
+ /// Day value.
+ /// Designated date/time format 1.
+ /// Designated date/time format 2.
+ [Theory]
+ [InlineData(2016, 12, 31, "yyyy-MM-dd", "yyyy/MM/dd")]
+ public void Given_Yaml_WithSpecificCultureAndValidDateTimeFormat_ReadYaml_ShouldReturn_Result(int year, int month, int day, string format1, string format2)
+ {
+ var yaml = $"{year}-{month:00}-{day:00}";
+
+ var parser = A.Fake();
+ A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml));
+
+ var culture = new CultureInfo("ko-KR"); // Sample specific culture
+ var converter = new DateOnlyConverter(provider: culture, formats: new[] { format1, format2 });
+
+ var result = converter.ReadYaml(parser, typeof(DateOnly));
+
+ result.Should().BeOfType();
+ ((DateOnly)result).Year.Should().Be(year);
+ ((DateOnly)result).Month.Should().Be(month);
+ ((DateOnly)result).Day.Should().Be(day);
+ }
+
+ ///
+ /// Tests whether the ReadYaml() method should return expected result or not.
+ ///
+ /// Date/Time format.
+ /// Date/Time value.
+ [Theory]
+ [InlineData("d", "01/11/2017")]
+ [InlineData("D", "Wednesday, 11 January 2017")]
+ [InlineData("f", "Wednesday, 11 January 2017 02:36")]
+ [InlineData("F", "Wednesday, 11 January 2017 02:36:16")]
+ [InlineData("g", "01/11/2017 02:36")]
+ [InlineData("G", "01/11/2017 02:36:16")]
+ [InlineData("M", "January 11")]
+ [InlineData("s", "2017-01-11T02:36:16")]
+ [InlineData("u", "2017-01-11 02:36:16Z")]
+ [InlineData("Y", "2017 January")]
+ public void Given_Yaml_WithDateTimeFormat_ReadYaml_ShouldReturn_Result(string format, string value)
+ {
+ var expected = DateOnly.ParseExact(value, format, CultureInfo.InvariantCulture);
+ var converter = new DateOnlyConverter(formats: new[] { "d", "D", "f", "F", "g", "G", "M", "s", "u", "Y" });
+
+ var parser = A.Fake();
+ A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(value));
+
+ var result = converter.ReadYaml(parser, typeof(DateOnly));
+
+ result.Should().Be(expected);
+ }
+
+ ///
+ /// Tests whether the ReadYaml() method should return expected result or not.
+ ///
+ /// Date/Time format.
+ /// Locale value.
+ /// Date/Time value.
+ [Theory]
+ [InlineData("d", "fr-FR", "13/01/2017")]
+ [InlineData("D", "fr-FR", "vendredi 13 janvier 2017")]
+ [InlineData("f", "fr-FR", "vendredi 13 janvier 2017 05:25")]
+ [InlineData("F", "fr-FR", "vendredi 13 janvier 2017 05:25:08")]
+ [InlineData("g", "fr-FR", "13/01/2017 05:25")]
+ [InlineData("G", "fr-FR", "13/01/2017 05:25:08")]
+ [InlineData("M", "fr-FR", "13 janvier")]
+ [InlineData("s", "fr-FR", "2017-01-13T05:25:08")]
+ [InlineData("u", "fr-FR", "2017-01-13 05:25:08Z")]
+ [InlineData("Y", "fr-FR", "janvier 2017")]
+ // [InlineData("d", "ko-KR", "2017-01-13")]
+ [InlineData("D", "ko-KR", "2017년 1월 13일 금요일")]
+ // [InlineData("f", "ko-KR", "2017년 1월 13일 금요일 오전 5:32")]
+ // [InlineData("F", "ko-KR", "2017년 1월 13일 금요일 오전 5:32:06")]
+ // [InlineData("g", "ko-KR", "2017-01-13 오전 5:32")]
+ // [InlineData("G", "ko-KR", "2017-01-13 오전 5:32:06")]
+ [InlineData("M", "ko-KR", "1월 13일")]
+ [InlineData("s", "ko-KR", "2017-01-13T05:32:06")]
+ [InlineData("u", "ko-KR", "2017-01-13 05:32:06Z")]
+ [InlineData("Y", "ko-KR", "2017년 1월")]
+ public void Given_Yaml_WithLocaleAndDateTimeFormat_ReadYaml_ShouldReturn_Result(string format, string locale, string value)
+ {
+ var culture = new CultureInfo(locale);
+
+ var expected = default(DateOnly);
+ try
+ {
+ expected = DateOnly.ParseExact(value, format, culture);
+ }
+ catch (Exception ex)
+ {
+ var message = string.Format("Failed to parse the test argument to DateOnly. The expected date/time format should look like this: '{0}'", DateTime.Now.ToString(format, culture));
+ throw new Exception(message, ex);
+ }
+
+ var converter = new DateOnlyConverter(provider: culture, formats: new[] { "d", "D", "f", "F", "g", "G", "M", "s", "u", "Y" });
+
+ var parser = A.Fake();
+ A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(value));
+
+ var result = converter.ReadYaml(parser, typeof(DateOnly));
+
+ result.Should().Be(expected);
+ }
+
+ ///
+ /// Tests whether the WriteYaml method should return expected result or not.
+ ///
+ /// Year value.
+ /// Month value.
+ /// Day value.
+ /// The converter instance uses its default parameter of "d".
+ [Theory]
+ [InlineData(2016, 12, 31)]
+ public void Given_Values_WriteYaml_ShouldReturn_Result(int year, int month, int day)
+ {
+ var dateOnly = new DateOnly(year, month, day);
+ var formatted = dateOnly.ToString("d", CultureInfo.InvariantCulture);
+ var obj = new TestObject() { DateOnly = dateOnly };
+
+ var builder = new SerializerBuilder();
+ builder.WithNamingConvention(CamelCaseNamingConvention.Instance);
+ builder.WithTypeConverter(new DateOnlyConverter());
+
+ var serialiser = builder.Build();
+
+ var serialised = serialiser.Serialize(obj);
+
+ serialised.Should().ContainEquivalentOf($"dateonly: {formatted}");
+ }
+
+ ///
+ /// Tests whether the WriteYaml method should return expected result or not.
+ ///
+ /// Year value.
+ /// Month value.
+ /// Day value.
+ /// Locale value.
+ /// The converter instance uses its default parameter of "d".
+ [Theory]
+ [InlineData(2016, 12, 31, "es-ES")]
+ [InlineData(2016, 12, 31, "ko-KR")]
+ public void Given_Values_WithLocale_WriteYaml_ShouldReturn_Result(int year, int month, int day, string locale)
+ {
+ var dateOnly = new DateOnly(year, month, day);
+ var culture = new CultureInfo(locale);
+ var formatted = dateOnly.ToString("d", culture);
+ var obj = new TestObject() { DateOnly = dateOnly };
+
+ var builder = new SerializerBuilder();
+ builder.WithNamingConvention(CamelCaseNamingConvention.Instance);
+ builder.WithTypeConverter(new DateOnlyConverter(provider: culture));
+
+ var serialiser = builder.Build();
+
+ var serialised = serialiser.Serialize(obj);
+
+ serialised.Should().ContainEquivalentOf($"dateonly: {formatted}");
+ }
+
+ ///
+ /// Tests whether the WriteYaml method should return expected result or not.
+ ///
+ /// Year value.
+ /// Month value.
+ /// Day value.
+ /// The converter instance uses its default parameter of "d".
+ [Theory]
+ [InlineData(2016, 12, 31)]
+ public void Given_Values_WithFormats_WriteYaml_ShouldReturn_Result_WithFirstFormat(int year, int month, int day)
+ {
+ var dateOnly = new DateOnly(year, month, day);
+ var format = "yyyy-MM-dd";
+ var formatted = dateOnly.ToString(format, CultureInfo.InvariantCulture);
+ var obj = new TestObject() { DateOnly = dateOnly };
+
+ var builder = new SerializerBuilder();
+ builder.WithNamingConvention(CamelCaseNamingConvention.Instance);
+ builder.WithTypeConverter(new DateOnlyConverter(formats: new[] { format, "d" }));
+
+ var serialiser = builder.Build();
+
+ var serialised = serialiser.Serialize(obj);
+
+ serialised.Should().ContainEquivalentOf($"dateonly: {formatted}");
+ }
+
+ [Fact]
+ public void JsonCompatible_EncaseDateOnlyInDoubleQuotes()
+ {
+ var serializer = new SerializerBuilder().JsonCompatible().Build();
+ var testObject = new TestObject { DateOnly = new DateOnly(2023, 01, 14) };
+ var actual = serializer.Serialize(testObject);
+
+ actual.TrimNewLines().Should().ContainEquivalentOf("{\"DateOnly\": \"01/14/2023\"}");
+ }
+
+ ///
+ /// This represents the test object entity.
+ ///
+ private class TestObject
+ {
+ ///
+ /// Gets or sets the value.
+ ///
+ public DateOnly DateOnly { get; set; }
+ }
+ }
+}
+#endif
diff --git a/YamlDotNet.Test/Serialization/TimeOnlyConverterTests.cs b/YamlDotNet.Test/Serialization/TimeOnlyConverterTests.cs
new file mode 100644
index 00000000..7d1be635
--- /dev/null
+++ b/YamlDotNet.Test/Serialization/TimeOnlyConverterTests.cs
@@ -0,0 +1,335 @@
+// This file is part of YamlDotNet - A .NET library for YAML.
+// Copyright (c) Antoine Aubry and contributors
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do
+// so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+#if NET6_0_OR_GREATER
+using System;
+using System.Globalization;
+using FakeItEasy;
+using FluentAssertions;
+using Xunit;
+using YamlDotNet.Core;
+using YamlDotNet.Core.Events;
+using YamlDotNet.Serialization;
+using YamlDotNet.Serialization.Converters;
+using YamlDotNet.Serialization.NamingConventions;
+
+namespace YamlDotNet.Test.Serialization
+{
+ ///
+ /// This represents the test entity for the class.
+ ///
+ public class TimeOnlyConverterTests
+ {
+ ///
+ /// Tests whether the Accepts() method should return expected result or not.
+ ///
+ /// to check.
+ /// Expected result.
+ [Theory]
+ [InlineData(typeof(TimeOnly), true)]
+ [InlineData(typeof(string), false)]
+ public void Given_Type_Accepts_ShouldReturn_Result(Type type, bool expected)
+ {
+ var converter = new TimeOnlyConverter();
+
+ var result = converter.Accepts(type);
+
+ result.Should().Be(expected);
+ }
+
+ ///
+ /// Tests whether the ReadYaml() method should throw or not.
+ ///
+ /// Hour value.
+ /// Minute value.
+ /// Second value.
+ /// The converter instance uses its default parameter of "T".
+ [Theory]
+ [InlineData(6, 12, 31)]
+ public void Given_Yaml_WithInvalidDateTimeFormat_WithDefaultParameters_ReadYaml_ShouldThrow_Exception(int hour, int minute, int second)
+ {
+ var yaml = $"{hour:00}-{minute:00}-{second:00}";
+
+ var parser = A.Fake();
+ A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml));
+
+ var converter = new TimeOnlyConverter();
+
+ Action action = () => { converter.ReadYaml(parser, typeof(TimeOnly)); };
+
+ action.ShouldThrow();
+ }
+
+ ///
+ /// Tests whether the ReadYaml() method should return expected result or not.
+ ///
+ /// Hour value.
+ /// Minute value.
+ /// Second value.
+ /// The converter instance uses its default parameter of "T".
+ [Theory]
+ [InlineData(6, 12, 31)]
+ public void Given_Yaml_WithValidDateTimeFormat_WithDefaultParameters_ReadYaml_ShouldReturn_Result(int hour, int minute, int second)
+ {
+ var yaml = $"{hour:00}:{minute:00}:{second:00}"; // This is the DateTime format of "T"
+
+ var parser = A.Fake();
+ A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml));
+
+ var converter = new TimeOnlyConverter();
+
+ var result = converter.ReadYaml(parser, typeof(TimeOnly));
+
+ result.Should().BeOfType();
+ ((TimeOnly)result).Hour.Should().Be(hour);
+ ((TimeOnly)result).Minute.Should().Be(minute);
+ ((TimeOnly)result).Second.Should().Be(second);
+ }
+
+ ///
+ /// Tests whether the ReadYaml() method should return expected result or not.
+ ///
+ /// Hour value.
+ /// Minute value.
+ /// Second value.
+ /// Designated date/time format 1.
+ /// Designated date/time format 2.
+ [Theory]
+ [InlineData(6, 12, 31, "HH-mm-ss", "HH:mm:ss")]
+ public void Given_Yaml_WithValidDateTimeFormat_ReadYaml_ShouldReturn_Result(int hour, int minute, int second, string format1, string format2)
+ {
+ var yaml = $"{hour:00}-{minute:00}-{second:00}";
+
+ var parser = A.Fake();
+ A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml));
+
+ var converter = new TimeOnlyConverter(formats: new[] { format1, format2 });
+
+ var result = converter.ReadYaml(parser, typeof(TimeOnly));
+
+ result.Should().BeOfType();
+ ((TimeOnly)result).Hour.Should().Be(6);
+ ((TimeOnly)result).Minute.Should().Be(12);
+ ((TimeOnly)result).Second.Should().Be(31);
+ }
+
+ ///
+ /// Tests whether the ReadYaml() method should return expected result or not.
+ ///
+ /// Hour value.
+ /// Minute value.
+ /// Second value.
+ /// Designated date/time format 1.
+ /// Designated date/time format 2.
+ [Theory]
+ [InlineData(6, 12, 31, "HH-mm-ss", "HH:mm:ss")]
+ public void Given_Yaml_WithSpecificCultureAndValidDateTimeFormat_ReadYaml_ShouldReturn_Result(int hour, int minute, int second, string format1, string format2)
+ {
+ var yaml = $"{hour:00}-{minute:00}-{second:00}";
+
+ var parser = A.Fake();
+ A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml));
+
+ var culture = new CultureInfo("ko-KR"); // Sample specific culture
+ var converter = new TimeOnlyConverter(provider: culture, formats: new[] { format1, format2 });
+
+ var result = converter.ReadYaml(parser, typeof(TimeOnly));
+
+ result.Should().BeOfType();
+ ((TimeOnly)result).Hour.Should().Be(6);
+ ((TimeOnly)result).Minute.Should().Be(12);
+ ((TimeOnly)result).Second.Should().Be(31);
+ }
+
+ ///
+ /// Tests whether the ReadYaml() method should return expected result or not.
+ ///
+ /// Date/Time format.
+ /// Date/Time value.
+ [Theory]
+ [InlineData("g", "01/11/2017 02:36")]
+ [InlineData("G", "01/11/2017 02:36:16")]
+ [InlineData("s", "2017-01-11T02:36:16")]
+ [InlineData("t", "02:36")]
+ [InlineData("T", "02:36:16")]
+ [InlineData("u", "2017-01-11 02:36:16Z")]
+ public void Given_Yaml_WithTimeFormat_ReadYaml_ShouldReturn_Result(string format, string value)
+ {
+ var expected = TimeOnly.ParseExact(value, format, CultureInfo.InvariantCulture);
+ var converter = new TimeOnlyConverter(formats: new[] { "g", "G", "s", "t", "T", "u" });
+
+ var parser = A.Fake();
+ A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(value));
+
+ var result = converter.ReadYaml(parser, typeof(TimeOnly));
+
+ result.Should().Be(expected);
+ }
+
+ ///
+ /// Tests whether the ReadYaml() method should return expected result or not.
+ ///
+ /// Date/Time format.
+ /// Locale value.
+ /// Date/Time value.
+ [Theory]
+ [InlineData("g", "fr-FR", "13/01/2017 05:25")]
+ [InlineData("G", "fr-FR", "13/01/2017 05:25:08")]
+ [InlineData("s", "fr-FR", "2017-01-13T05:25:08")]
+ [InlineData("t", "fr-FR", "05:25")]
+ [InlineData("T", "fr-FR", "05:25:08")]
+ [InlineData("u", "fr-FR", "2017-01-13 05:25:08Z")]
+ // [InlineData("g", "ko-KR", "2017-01-13 오전 5:32")]
+ // [InlineData("G", "ko-KR", "2017-01-13 오전 5:32:06")]
+ [InlineData("s", "ko-KR", "2017-01-13T05:32:06")]
+ // [InlineData("t", "ko-KR", "오전 5:32")]
+ // [InlineData("T", "ko-KR", "오전 5:32:06")]
+ [InlineData("u", "ko-KR", "2017-01-13 05:32:06Z")]
+ public void Given_Yaml_WithLocaleAndTimeFormat_ReadYaml_ShouldReturn_Result(string format, string locale, string value)
+ {
+ var culture = new CultureInfo(locale);
+
+ var expected = default(TimeOnly);
+ try
+ {
+ expected = TimeOnly.ParseExact(value, format, culture);
+ }
+ catch (Exception ex)
+ {
+ var message = string.Format("Failed to parse the test argument to TimeOnly. The expected date/time format should look like this: '{0}'", DateTime.Now.ToString(format, culture));
+ throw new Exception(message, ex);
+ }
+
+ var converter = new TimeOnlyConverter(provider: culture, formats: new[] { "g", "G", "s", "t", "T", "u" });
+
+ var parser = A.Fake();
+ A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(value));
+
+ var result = converter.ReadYaml(parser, typeof(TimeOnly));
+
+ result.Should().Be(expected);
+ }
+
+ ///
+ /// Tests whether the WriteYaml method should return expected result or not.
+ ///
+ /// Hour value.
+ /// Minute value.
+ /// Second value.
+ /// The converter instance uses its default parameter of "T".
+ [Theory]
+ [InlineData(6, 12, 31)]
+ public void Given_Values_WriteYaml_ShouldReturn_Result(int hour, int minute, int second)
+ {
+ var timeOnly = new TimeOnly(hour, minute, second);
+ var formatted = timeOnly.ToString("T", CultureInfo.InvariantCulture);
+ var obj = new TestObject() { TimeOnly = timeOnly };
+
+ var builder = new SerializerBuilder();
+ builder.WithNamingConvention(CamelCaseNamingConvention.Instance);
+ builder.WithTypeConverter(new TimeOnlyConverter());
+
+ var serialiser = builder.Build();
+
+ var serialised = serialiser.Serialize(obj);
+
+ serialised.Should().ContainEquivalentOf($"timeonly: {formatted}");
+ }
+
+ ///
+ /// Tests whether the WriteYaml method should return expected result or not.
+ ///
+ /// Hour value.
+ /// Minute value.
+ /// Second value.
+ /// Locale value.
+ /// The converter instance uses its default parameter of "T".
+ [Theory]
+ [InlineData(6, 12, 31, "es-ES")]
+ [InlineData(6, 12, 31, "ko-KR")]
+ public void Given_Values_WithLocale_WriteYaml_ShouldReturn_Result(int hour, int minute, int second, string locale)
+ {
+ var timeOnly = new TimeOnly(hour, minute, second);
+ var culture = new CultureInfo(locale);
+ var formatted = timeOnly.ToString("T", culture);
+ var obj = new TestObject() { TimeOnly = timeOnly };
+
+ var builder = new SerializerBuilder();
+ builder.WithNamingConvention(CamelCaseNamingConvention.Instance);
+ builder.WithTypeConverter(new TimeOnlyConverter(provider: culture));
+
+ var serialiser = builder.Build();
+
+ var serialised = serialiser.Serialize(obj);
+
+ serialised.Should().ContainEquivalentOf($"timeonly: {formatted}");
+ }
+
+ ///
+ /// Tests whether the WriteYaml method should return expected result or not.
+ ///
+ /// Hour value.
+ /// Minute value.
+ /// Second value.
+ /// The converter instance uses its default parameter of "T".
+ [Theory]
+ [InlineData(6, 12, 31)]
+ public void Given_Values_WithFormats_WriteYaml_ShouldReturn_Result_WithFirstFormat(int hour, int minute, int second)
+ {
+ var timeOnly = new TimeOnly(hour, minute, second);
+ var format = "HH:mm:ss";
+ var formatted = timeOnly.ToString(format, CultureInfo.InvariantCulture);
+ var obj = new TestObject() { TimeOnly = timeOnly };
+
+ var builder = new SerializerBuilder();
+ builder.WithNamingConvention(CamelCaseNamingConvention.Instance);
+ builder.WithTypeConverter(new TimeOnlyConverter(formats: new[] { format, "T" }));
+
+ var serialiser = builder.Build();
+
+ var serialised = serialiser.Serialize(obj);
+
+ serialised.Should().ContainEquivalentOf($"timeonly: {formatted}");
+ }
+
+ [Fact]
+ public void JsonCompatible_EncaseTimeOnlyInDoubleQuotes()
+ {
+ var serializer = new SerializerBuilder().JsonCompatible().Build();
+ var testObject = new TestObject { TimeOnly = new TimeOnly(6, 12, 31) };
+ var actual = serializer.Serialize(testObject);
+
+ actual.TrimNewLines().Should().ContainEquivalentOf("{\"TimeOnly\": \"06:12:31\"}");
+ }
+
+ ///
+ /// This represents the test object entity.
+ ///
+ private class TestObject
+ {
+ ///
+ /// Gets or sets the value.
+ ///
+ public TimeOnly TimeOnly { get; set; }
+ }
+ }
+}
+#endif
diff --git a/YamlDotNet/Serialization/Converters/DateOnlyConverter.cs b/YamlDotNet/Serialization/Converters/DateOnlyConverter.cs
new file mode 100644
index 00000000..6bf04d47
--- /dev/null
+++ b/YamlDotNet/Serialization/Converters/DateOnlyConverter.cs
@@ -0,0 +1,96 @@
+// This file is part of YamlDotNet - A .NET library for YAML.
+// Copyright (c) Antoine Aubry and contributors
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do
+// so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+#if NET6_0_OR_GREATER
+using System;
+using System.Globalization;
+using System.Linq;
+
+using YamlDotNet.Core;
+using YamlDotNet.Core.Events;
+
+namespace YamlDotNet.Serialization.Converters
+{
+ ///
+ /// This represents the YAML converter entity for .
+ ///
+ public class DateOnlyConverter : IYamlTypeConverter
+ {
+ private readonly IFormatProvider provider;
+ private readonly bool doubleQuotes;
+ private readonly string[] formats;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// instance. Default value is .
+ /// If true, will use double quotes when writing the value to the stream.
+ /// List of date/time formats for parsing. Default value is "d".
+ /// On deserializing, all formats in the list are used for conversion, while on serializing, the first format in the list is used.
+ public DateOnlyConverter(IFormatProvider? provider = null, bool doubleQuotes = false, params string[] formats)
+ {
+ this.provider = provider ?? CultureInfo.InvariantCulture;
+ this.doubleQuotes = doubleQuotes;
+ this.formats = formats.DefaultIfEmpty("d").ToArray();
+ }
+
+ ///
+ /// Gets a value indicating whether the current converter supports converting the specified type.
+ ///
+ /// to check.
+ /// Returns True, if the current converter supports; otherwise returns False.
+ public bool Accepts(Type type)
+ {
+ return type == typeof(DateOnly);
+ }
+
+ ///
+ /// Reads an object's state from a YAML parser.
+ ///
+ /// instance.
+ /// to convert.
+ /// Returns the instance converted.
+ /// On deserializing, all formats in the list are used for conversion.
+ public object ReadYaml(IParser parser, Type type)
+ {
+ var value = parser.Consume().Value;
+
+ var dateOnly = DateOnly.ParseExact(value, this.formats, this.provider);
+ return dateOnly;
+ }
+
+ ///
+ /// Writes the specified object's state to a YAML emitter.
+ ///
+ /// instance.
+ /// Value to write.
+ /// to convert.
+ /// On serializing, the first format in the list is used.
+ public void WriteYaml(IEmitter emitter, object? value, Type type)
+ {
+ var dateOnly = (DateOnly)value!;
+ var formatted = dateOnly.ToString(this.formats.First(), this.provider); // Always take the first format of the list.
+
+ emitter.Emit(new Scalar(AnchorName.Empty, TagName.Empty, formatted, doubleQuotes ? ScalarStyle.DoubleQuoted : ScalarStyle.Any, true, false));
+ }
+ }
+}
+#endif
diff --git a/YamlDotNet/Serialization/Converters/TimeOnlyConverter.cs b/YamlDotNet/Serialization/Converters/TimeOnlyConverter.cs
new file mode 100644
index 00000000..05e53a2d
--- /dev/null
+++ b/YamlDotNet/Serialization/Converters/TimeOnlyConverter.cs
@@ -0,0 +1,96 @@
+// This file is part of YamlDotNet - A .NET library for YAML.
+// Copyright (c) Antoine Aubry and contributors
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do
+// so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+#if NET6_0_OR_GREATER
+using System;
+using System.Globalization;
+using System.Linq;
+
+using YamlDotNet.Core;
+using YamlDotNet.Core.Events;
+
+namespace YamlDotNet.Serialization.Converters
+{
+ ///
+ /// This represents the YAML converter entity for .
+ ///
+ public class TimeOnlyConverter : IYamlTypeConverter
+ {
+ private readonly IFormatProvider provider;
+ private readonly bool doubleQuotes;
+ private readonly string[] formats;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// instance. Default value is .
+ /// If true, will use double quotes when writing the value to the stream.
+ /// List of date/time formats for parsing. Default value is "T".
+ /// On deserializing, all formats in the list are used for conversion, while on serializing, the first format in the list is used.
+ public TimeOnlyConverter(IFormatProvider? provider = null, bool doubleQuotes = false, params string[] formats)
+ {
+ this.provider = provider ?? CultureInfo.InvariantCulture;
+ this.doubleQuotes = doubleQuotes;
+ this.formats = formats.DefaultIfEmpty("T").ToArray();
+ }
+
+ ///
+ /// Gets a value indicating whether the current converter supports converting the specified type.
+ ///
+ /// to check.
+ /// Returns True, if the current converter supports; otherwise returns False.
+ public bool Accepts(Type type)
+ {
+ return type == typeof(TimeOnly);
+ }
+
+ ///
+ /// Reads an object's state from a YAML parser.
+ ///
+ /// instance.
+ /// to convert.
+ /// Returns the instance converted.
+ /// On deserializing, all formats in the list are used for conversion.
+ public object ReadYaml(IParser parser, Type type)
+ {
+ var value = parser.Consume().Value;
+
+ var timeOnly = TimeOnly.ParseExact(value, this.formats, this.provider);
+ return timeOnly;
+ }
+
+ ///
+ /// Writes the specified object's state to a YAML emitter.
+ ///
+ /// instance.
+ /// Value to write.
+ /// to convert.
+ /// On serializing, the first format in the list is used.
+ public void WriteYaml(IEmitter emitter, object? value, Type type)
+ {
+ var timeOnly = (TimeOnly)value!;
+ var formatted = timeOnly.ToString(this.formats.First(), this.provider); // Always take the first format of the list.
+
+ emitter.Emit(new Scalar(AnchorName.Empty, TagName.Empty, formatted, doubleQuotes ? ScalarStyle.DoubleQuoted : ScalarStyle.Any, true, false));
+ }
+ }
+}
+#endif
diff --git a/YamlDotNet/Serialization/SerializerBuilder.cs b/YamlDotNet/Serialization/SerializerBuilder.cs
index df462672..8b817f7c 100755
--- a/YamlDotNet/Serialization/SerializerBuilder.cs
+++ b/YamlDotNet/Serialization/SerializerBuilder.cs
@@ -335,6 +335,10 @@ public SerializerBuilder JsonCompatible()
return this
.WithTypeConverter(new GuidConverter(true), w => w.InsteadOf())
.WithTypeConverter(new DateTimeConverter(doubleQuotes: true))
+#if NET6_0_OR_GREATER
+ .WithTypeConverter(new DateOnlyConverter(doubleQuotes: true))
+ .WithTypeConverter(new TimeOnlyConverter(doubleQuotes: true))
+#endif
.WithEventEmitter(inner => new JsonEventEmitter(inner, yamlFormatter), loc => loc.InsteadOf());
}
diff --git a/YamlDotNet/Serialization/StaticSerializerBuilder.cs b/YamlDotNet/Serialization/StaticSerializerBuilder.cs
index 973c7a5b..99b332fa 100644
--- a/YamlDotNet/Serialization/StaticSerializerBuilder.cs
+++ b/YamlDotNet/Serialization/StaticSerializerBuilder.cs
@@ -340,6 +340,11 @@ public StaticSerializerBuilder JsonCompatible()
return this
.WithTypeConverter(new GuidConverter(true), w => w.InsteadOf())
+ .WithTypeConverter(new DateTimeConverter(doubleQuotes: true))
+#if NET6_0_OR_GREATER
+ .WithTypeConverter(new DateOnlyConverter(doubleQuotes: true))
+ .WithTypeConverter(new TimeOnlyConverter(doubleQuotes: true))
+#endif
.WithEventEmitter(inner => new JsonEventEmitter(inner, yamlFormatter), loc => loc.InsteadOf());
}