From 36504710d11d238ee11917c3fbe4960a4843b522 Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Thu, 27 Jan 2022 12:20:37 -0800 Subject: [PATCH 1/2] Fix DateOnly and TimeOnly Formatting using interpolated strings --- .../System.Private.CoreLib/src/System/DateOnly.cs | 6 ++---- .../src/System/Globalization/DateTimeFormat.cs | 8 ++++---- .../System.Private.CoreLib/src/System/ISpanFormattable.cs | 1 + .../System.Private.CoreLib/src/System/TimeOnly.cs | 6 ++---- .../System.Runtime/tests/System/DateOnlyTests.cs | 8 ++++++++ .../System.Runtime/tests/System/TimeOnlyTests.cs | 8 ++++++++ 6 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs b/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs index 2dcf4903f27011..955ecb17b79c34 100644 --- a/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs +++ b/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs @@ -817,15 +817,13 @@ public string ToString(string? format, IFormatProvider? provider) return DateTimeFormat.TryFormat(GetEquivalentDateTime(), destination, out charsWritten, format, provider); default: - charsWritten = 0; - return false; + throw new FormatException(SR.Argument_BadFormatSpecifier); } } if (!DateTimeFormat.IsValidCustomDateFormat(format, throwOnError: false)) { - charsWritten = 0; - return false; + throw new FormatException(SR.Format(SR.Format_DateTimeOnlyContainsNoneDateParts, format.ToString(), nameof(DateOnly))); } return DateTimeFormat.TryFormat(GetEquivalentDateTime(), destination, out charsWritten, format, provider); diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs index e72e7885566401..f8c1745c1e4994 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs @@ -62,8 +62,8 @@ Patterns Format Description Example "M" "0" month w/o leading zero 2 "MM" "00" month with leading zero 02 "MMM" short month name (abbreviation) Feb - "MMMM" full month name Febuary - "MMMM*" full month name Febuary + "MMMM" full month name February + "MMMM*" full month name February "y" "0" two digit year (year % 100) w/o leading zero 0 "yy" "00" two digit year (year % 100) with leading zero 00 @@ -734,7 +734,7 @@ private static StringBuilder FormatCustomized( break; case '\\': // Escaped character. Can be used to insert a character into the format string. - // For exmple, "\d" will insert the character 'd' into the string. + // For example, "\d" will insert the character 'd' into the string. // // NOTENOTE : we can remove this format character if we enforce the enforced quote // character rule. @@ -966,7 +966,7 @@ private static string ExpandPredefinedFormat(ReadOnlySpan format, ref Date // This format is not supported by DateTimeOffset throw new FormatException(SR.Format_InvalidString); } - // Universal time is always in Greogrian calendar. + // Universal time is always in Gregorian calendar. // // Change the Calendar to be Gregorian Calendar. // diff --git a/src/libraries/System.Private.CoreLib/src/System/ISpanFormattable.cs b/src/libraries/System.Private.CoreLib/src/System/ISpanFormattable.cs index 0998785f780ba3..15e7717435d960 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ISpanFormattable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ISpanFormattable.cs @@ -15,6 +15,7 @@ public interface ISpanFormattable : IFormattable /// /// An implementation of this interface should produce the same string of characters as an implementation of /// on the same type. + /// TryFormat should return false only if there is not enough space in the destination buffer. Any other failures should throw an exception. /// bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider); } diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs b/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs index aee22dc9297f2f..9fb102f3990ac8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs @@ -892,15 +892,13 @@ public string ToString(string? format, IFormatProvider? provider) return DateTimeFormat.TryFormat(ToDateTime(), destination, out charsWritten, format, provider); default: - charsWritten = 0; - return false; + throw new FormatException(SR.Argument_BadFormatSpecifier); } } if (!DateTimeFormat.IsValidCustomTimeFormat(format, throwOnError: false)) { - charsWritten = 0; - return false; + throw new FormatException(SR.Format(SR.Format_DateTimeOnlyContainsNoneDateParts, format.ToString(), nameof(TimeOnly))); } return DateTimeFormat.TryFormat(ToDateTime(), destination, out charsWritten, format, provider); diff --git a/src/libraries/System.Runtime/tests/System/DateOnlyTests.cs b/src/libraries/System.Runtime/tests/System/DateOnlyTests.cs index 935c75e01bb7c3..27f0b359bbf601 100644 --- a/src/libraries/System.Runtime/tests/System/DateOnlyTests.cs +++ b/src/libraries/System.Runtime/tests/System/DateOnlyTests.cs @@ -514,5 +514,13 @@ public static void TryFormatTest() Assert.False(dateOnly.TryFormat(buffer.Slice(0, 3), out charsWritten, "r")); Assert.False(dateOnly.TryFormat(buffer.Slice(0, 3), out charsWritten, "O")); } + + [Fact] + public static void InvalidFormattingWithInterpolatedStringTest() + { + DateOnly dateOnly = DateOnly.FromDateTime(DateTime.Today); + Assert.Throws(() => $"{dateOnly:u}"); + Assert.Throws(() => $"{dateOnly:hh-ss}"); + } } } diff --git a/src/libraries/System.Runtime/tests/System/TimeOnlyTests.cs b/src/libraries/System.Runtime/tests/System/TimeOnlyTests.cs index b39e94e00d1dc9..e427ee2341af27 100644 --- a/src/libraries/System.Runtime/tests/System/TimeOnlyTests.cs +++ b/src/libraries/System.Runtime/tests/System/TimeOnlyTests.cs @@ -484,5 +484,13 @@ public static void TryFormatTest() Assert.False(timeOnly.TryFormat(buffer.Slice(0, 3), out charsWritten, "r")); Assert.False(timeOnly.TryFormat(buffer.Slice(0, 3), out charsWritten, "O")); } + + [Fact] + public static void InvalidFormattingWithInterpolatedStringTest() + { + TimeOnly timeOnly = TimeOnly.FromDateTime(DateTime.Now); + Assert.Throws(() => $"{timeOnly:u}"); + Assert.Throws(() => $"{timeOnly:dd-yyyy}"); + } } } From 7caf64a27cf3ae772e58f61e064f9e0f45bc93a4 Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Thu, 27 Jan 2022 13:17:36 -0800 Subject: [PATCH 2/2] Add TryFormat tests --- .../System.Runtime/tests/System/DateOnlyTests.cs | 14 ++++++++------ .../System.Runtime/tests/System/TimeOnlyTests.cs | 13 ++++++++----- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System/DateOnlyTests.cs b/src/libraries/System.Runtime/tests/System/DateOnlyTests.cs index 27f0b359bbf601..624b9b508a868e 100644 --- a/src/libraries/System.Runtime/tests/System/DateOnlyTests.cs +++ b/src/libraries/System.Runtime/tests/System/DateOnlyTests.cs @@ -513,12 +513,14 @@ public static void TryFormatTest() Assert.False(dateOnly.TryFormat(buffer.Slice(0, 3), out charsWritten)); Assert.False(dateOnly.TryFormat(buffer.Slice(0, 3), out charsWritten, "r")); Assert.False(dateOnly.TryFormat(buffer.Slice(0, 3), out charsWritten, "O")); - } - - [Fact] - public static void InvalidFormattingWithInterpolatedStringTest() - { - DateOnly dateOnly = DateOnly.FromDateTime(DateTime.Today); + Assert.Throws(() => { + Span buff = stackalloc char[100]; + dateOnly.TryFormat(buff, out charsWritten, "u"); + }); + Assert.Throws(() => { + Span buff = stackalloc char[100]; + dateOnly.TryFormat(buff, out charsWritten, "hh-ss"); + }); Assert.Throws(() => $"{dateOnly:u}"); Assert.Throws(() => $"{dateOnly:hh-ss}"); } diff --git a/src/libraries/System.Runtime/tests/System/TimeOnlyTests.cs b/src/libraries/System.Runtime/tests/System/TimeOnlyTests.cs index e427ee2341af27..f74a4012747489 100644 --- a/src/libraries/System.Runtime/tests/System/TimeOnlyTests.cs +++ b/src/libraries/System.Runtime/tests/System/TimeOnlyTests.cs @@ -483,12 +483,15 @@ public static void TryFormatTest() Assert.False(timeOnly.TryFormat(buffer.Slice(0, 3), out charsWritten)); Assert.False(timeOnly.TryFormat(buffer.Slice(0, 3), out charsWritten, "r")); Assert.False(timeOnly.TryFormat(buffer.Slice(0, 3), out charsWritten, "O")); - } - [Fact] - public static void InvalidFormattingWithInterpolatedStringTest() - { - TimeOnly timeOnly = TimeOnly.FromDateTime(DateTime.Now); + Assert.Throws(() => { + Span buff = stackalloc char[100]; + timeOnly.TryFormat(buff, out charsWritten, "u"); + }); + Assert.Throws(() => { + Span buff = stackalloc char[100]; + timeOnly.TryFormat(buff, out charsWritten, "dd-yyyy"); + }); Assert.Throws(() => $"{timeOnly:u}"); Assert.Throws(() => $"{timeOnly:dd-yyyy}"); }