From e595faf8ca33a6b96671ac7411e0dcf8be8e94e3 Mon Sep 17 00:00:00 2001 From: Vladimir Pecanac Date: Sun, 30 Aug 2026 18:24:14 +0200 Subject: [PATCH] DateTimeFormatInCSharp: retarget net10.0, MSTest 4.3.3, deterministic assertions Retarget both projects from net6.0 to net10.0 and lift the 2021-era test packages with it: Microsoft.NET.Test.Sdk 18.9.0, MSTest.TestAdapter and MSTest.TestFramework 4.3.3, coverlet.collector 10.0.1. MSTest 4 removed [ExpectedException], so the ja-JP ParseExact test now uses Assert.ThrowsExactly. Three assertions only passed on a machine whose clock is UTC and now hold anywhere: - %K on a DateTimeKind.Local value prints the machine's offset, so the expectation is computed from TimeZoneInfo.Local instead of hard-coded +00:00. - The RFC 1123 and universal-sortable tests called ToUniversalTime() on an Unspecified value, which converts from the local zone. Both values are now created as Utc, which is what the tests are about anyway. The character between the time and its AM/PM designator comes from the host's ICU data, not from the .NET version (dotnet/runtime#96022, "working as designed"), so en-US emits U+0020 on some hosts and U+202F on others. Console output is compared through TextExpectation.NormalizeSpaces, which folds Unicode space separators to a plain space, rather than pasting an invisible character into a string literal. Program.cs is left as it is. --- .../DateTimeFormatInCSharp.csproj | 2 +- .../Test/CustomFormatTests.cs | 53 ++++++++++--------- .../Test/StandardDateFormatTests.cs | 13 ++--- .../Test/StandardDateTimeFormatTests.cs | 26 +++++---- .../Test/StandardTimeFormatTests.cs | 4 +- .../DateTimeFormatInCSharp/Test/Test.csproj | 10 ++-- .../Test/TextExpectation.cs | 22 ++++++++ 7 files changed, 81 insertions(+), 49 deletions(-) create mode 100644 dotnet-datetime/DateTimeFormatInCSharp/Test/TextExpectation.cs diff --git a/dotnet-datetime/DateTimeFormatInCSharp/DateTimeFormatInCSharp/DateTimeFormatInCSharp.csproj b/dotnet-datetime/DateTimeFormatInCSharp/DateTimeFormatInCSharp/DateTimeFormatInCSharp.csproj index 74abf5c976..dfb40caafc 100644 --- a/dotnet-datetime/DateTimeFormatInCSharp/DateTimeFormatInCSharp/DateTimeFormatInCSharp.csproj +++ b/dotnet-datetime/DateTimeFormatInCSharp/DateTimeFormatInCSharp/DateTimeFormatInCSharp.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net10.0 enable enable diff --git a/dotnet-datetime/DateTimeFormatInCSharp/Test/CustomFormatTests.cs b/dotnet-datetime/DateTimeFormatInCSharp/Test/CustomFormatTests.cs index bee4040682..e750be9eb5 100644 --- a/dotnet-datetime/DateTimeFormatInCSharp/Test/CustomFormatTests.cs +++ b/dotnet-datetime/DateTimeFormatInCSharp/Test/CustomFormatTests.cs @@ -17,7 +17,7 @@ public void WhenUsingCustomFormatString_ThenValuesAreFromattedOrParsed() var datetime = new DateTime(2017, 8, 24, 14, 35, 0); Console.WriteLine("{0:MM/dd/yy H:mm:ss}", datetime); // 08/24/17 14:35:00 - Assert.AreEqual($"08/24/17 14:35:00{Environment.NewLine}", sw.ToString()); + Assert.AreEqual($"08/24/17 14:35:00{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -31,7 +31,7 @@ public void WhenUsingDayFormatSpecifier_ThenDayValuesAreShown() Console.WriteLine(datetime.ToString("dd MMMM", CultureInfo.CreateSpecificCulture("en-US"))); // 04 August Assert.AreEqual($"4 August{Environment.NewLine}" + - $"04 August{Environment.NewLine}", sw.ToString()); + $"04 August{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -45,7 +45,7 @@ public void WhenUsingDayFormatSpecifier_ThenWeekDaysAreShown() Console.WriteLine(datetime.ToString("dddd, d MMMM", new CultureInfo("en-US"))); // Friday, 4 August Assert.AreEqual($"Fri, 4 August{Environment.NewLine}" + - $"Friday, 4 August{Environment.NewLine}", sw.ToString()); + $"Friday, 4 August{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -59,7 +59,7 @@ public void WhenUsingMonthFormatSpecifier_ThenMonthValuesAreShown() Console.WriteLine(datetime.ToString("MM", CultureInfo.CreateSpecificCulture("en-US"))); // 08 Assert.AreEqual($"8{Environment.NewLine}" + - $"08{Environment.NewLine}", sw.ToString()); + $"08{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -73,7 +73,7 @@ public void WhenUsingMonthFormatSpecifier_ThenMonthsAreShown() Console.WriteLine(datetime.ToString("MMMM", CultureInfo.CreateSpecificCulture("en-US"))); // August Assert.AreEqual($"Aug{Environment.NewLine}" + - $"August{Environment.NewLine}", sw.ToString()); + $"August{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -87,7 +87,7 @@ public void WhenUsingSingleCharacterCustomIdentifier_ThenWorksAsCustomFormatStri Console.WriteLine(datetime.ToString(" y", CultureInfo.CreateSpecificCulture("en-US")).Trim()); // 17 Assert.AreEqual($"4{Environment.NewLine}" + - $"17{Environment.NewLine}", sw.ToString()); + $"17{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -105,7 +105,7 @@ public void WhenUsingYearCustomFormatIdentifier_ThenIsShownWithOneToFourChars() Assert.AreEqual($"1{Environment.NewLine}" + $"01{Environment.NewLine}" + $"2001{Environment.NewLine}" + - $"2001{Environment.NewLine}", sw.ToString()); + $"2001{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -119,7 +119,7 @@ public void WhenUsingHourFormatSpecifier_ThenOnetoTwelveDigitsAreShown() Console.WriteLine(datetime.ToString("hh", CultureInfo.CreateSpecificCulture("en-US"))); // 10 Assert.AreEqual($"10{Environment.NewLine}" + - $"10{Environment.NewLine}", sw.ToString()); + $"10{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -133,7 +133,7 @@ public void WhenUsingHourFormatSpecifier_ThenOnetoTwentyThreeDigitsAreShown() Console.WriteLine(datetime.ToString("HH", CultureInfo.CreateSpecificCulture("en-US"))); // 22 Assert.AreEqual($"22{Environment.NewLine}" + - $"22{Environment.NewLine}", sw.ToString()); + $"22{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -147,7 +147,7 @@ public void WhenUsingMinuteFormatSpecifier_ThenMinutesAreShown() Console.WriteLine(datetime.ToString("mm", CultureInfo.CreateSpecificCulture("en-US"))); // 35 Assert.AreEqual($"35{Environment.NewLine}" + - $"35{Environment.NewLine}", sw.ToString()); + $"35{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -161,7 +161,7 @@ public void WhenUsingSecondFormatSpecifier_ThenSecondsAreShown() Console.WriteLine(datetime.ToString("ss", CultureInfo.CreateSpecificCulture("en-US"))); // 15 Assert.AreEqual($"15{Environment.NewLine}" + - $"15{Environment.NewLine}", sw.ToString()); + $"15{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -185,7 +185,7 @@ public void WhenUsingSecondFractionsFormatSpecifier_ThenSecondsFractionsAreShown $"22:35:15.0180{Environment.NewLine}" + $"22:35:15.01800{Environment.NewLine}" + $"22:35:15.018000{Environment.NewLine}" + - $"22:35:15.0180000{Environment.NewLine}", sw.ToString()); + $"22:35:15.0180000{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -209,7 +209,7 @@ public void WhenUsingSecondFractionsFormatSpecifier_ThenNonZeroSecondsFractionsA $"22:35:15.018{Environment.NewLine}" + $"22:35:15.018{Environment.NewLine}" + $"22:35:15.018{Environment.NewLine}" + - $"22:35:15.018{Environment.NewLine}", sw.ToString()); + $"22:35:15.018{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -223,7 +223,7 @@ public void WhenUsingMeridienFormatSpecifier_ThenMeridienLabelIsShown() Console.WriteLine(datetime.ToString("hh:mm:ss tt", CultureInfo.CreateSpecificCulture("en-US"))); // 10:35:15 PM Assert.AreEqual($"10:35:15 P{Environment.NewLine}" + - $"10:35:15 PM{Environment.NewLine}", sw.ToString()); + $"10:35:15 PM{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -233,13 +233,18 @@ public void WhenUsingTimeZoneFormatSpecifier_ThenDependsOnKindProperty() Console.SetOut(sw); var datetimeLocal = new DateTime(2017, 8, 4, 22, 35, 15, DateTimeKind.Local); - Console.WriteLine(datetimeLocal.ToString("%K", CultureInfo.CreateSpecificCulture("en-US"))); // +00:00 + Console.WriteLine(datetimeLocal.ToString("%K", CultureInfo.CreateSpecificCulture("en-US"))); // the machine's UTC offset, e.g. +02:00 var datetimeUtc = new DateTime(2017, 8, 4, 22, 35, 15, DateTimeKind.Utc); Console.WriteLine(datetimeUtc.ToString("%K", CultureInfo.CreateSpecificCulture("en-US"))); // Z - Assert.AreEqual($"+00:00{Environment.NewLine}" + - $"Z{Environment.NewLine}", sw.ToString()); + // K on a Local value prints the machine's offset, so the expectation is computed + // rather than hard-coded -- a literal "+00:00" only passes where the clock is UTC. + var localOffset = TimeZoneInfo.Local.GetUtcOffset(datetimeLocal); + var expectedLocalOffset = (localOffset < TimeSpan.Zero ? "-" : "+") + localOffset.ToString(@"hh\:mm"); + + Assert.AreEqual($"{expectedLocalOffset}{Environment.NewLine}" + + $"Z{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -255,7 +260,7 @@ public void WhenUsingTimeZoneFormatSpecifier_ThenDependsOnInternalOffsetProperty Console.WriteLine(datetimeUtc.ToString("%K", CultureInfo.CreateSpecificCulture("en-US"))); // +00:00 Assert.AreEqual($"+02:00{Environment.NewLine}" + - $"+00:00{Environment.NewLine}", sw.ToString()); + $"+00:00{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -271,7 +276,7 @@ public void WhenUsingTimeZoneFormatSpecifier_ThenSignedTimeZoneIsShown() Assert.AreEqual($"+2{Environment.NewLine}" + $"+02{Environment.NewLine}" + - $"+02:00{Environment.NewLine}", sw.ToString()); + $"+02:00{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -287,7 +292,7 @@ public void WhenUsingEraFormatSpecifier_ThenEraIsShown() Assert.AreEqual($"AD{Environment.NewLine}" + $"AD{Environment.NewLine}" + - $"d. C.{Environment.NewLine}", sw.ToString()); + $"d. C.{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -302,7 +307,7 @@ public void WhenUsingCustomTimeSeparator_ThenCustomSeparatorIsUsed() Console.WriteLine(datetime.ToString("HH:mm:ss", formatInfo)); // 04;22;35 - Assert.AreEqual($"04;22;35{Environment.NewLine}", sw.ToString()); + Assert.AreEqual($"04;22;35{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -316,7 +321,7 @@ public void WhenUsingCustomDateSeparator_ThenCustomSeparatorIsUsed() formatInfo.DateSeparator = "-"; Console.WriteLine(datetime.ToString("dd/MM/yyyy", formatInfo)); // 24-08-2017 - Assert.AreEqual($"24-08-2017{Environment.NewLine}", sw.ToString()); + Assert.AreEqual($"24-08-2017{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -328,7 +333,7 @@ public void WhenUsingLiteralCharacters_ThenCharactersAppearUnchanged() var datetime = new DateTime(2017, 8, 24, 4, 22, 35, 15); Console.WriteLine(datetime.ToString("dd/MM/yyyy hh:mm:ss EST", CultureInfo.CreateSpecificCulture("en-US"))); // 24/08/2017 04:22:35 EST - Assert.AreEqual($"24/08/2017 04:22:35 EST{Environment.NewLine}", sw.ToString()); + Assert.AreEqual($"24/08/2017 04:22:35 EST{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -342,7 +347,7 @@ public void WhenUsingFormatSpecifiersAsLiteralCharacters_ThenCharactersAppearInT Console.WriteLine(datetime.ToString("dd/MM/yyyy hh:mm:ss 'gmt'", CultureInfo.CreateSpecificCulture("en-US"))); // 24/08/2017 04:22:35 gmt Assert.AreEqual($"24/08/2017 04:22:35 gmt{Environment.NewLine}" + - $"24/08/2017 04:22:35 gmt{Environment.NewLine}", sw.ToString()); + $"24/08/2017 04:22:35 gmt{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } } } diff --git a/dotnet-datetime/DateTimeFormatInCSharp/Test/StandardDateFormatTests.cs b/dotnet-datetime/DateTimeFormatInCSharp/Test/StandardDateFormatTests.cs index b1440eab17..5e611d6627 100644 --- a/dotnet-datetime/DateTimeFormatInCSharp/Test/StandardDateFormatTests.cs +++ b/dotnet-datetime/DateTimeFormatInCSharp/Test/StandardDateFormatTests.cs @@ -18,7 +18,7 @@ public void WhenUsingStandardFormatSpecifiers_ThenCurrentCultureFormatIsApplied( var datetime = new DateTime(2017, 8, 24); Console.WriteLine(datetime.ToString("d")); // 8/24/2017 - Assert.AreEqual($"8/24/2017{Environment.NewLine}", sw.ToString()); + Assert.AreEqual($"8/24/2017{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -34,7 +34,7 @@ public void WhenUsingStandardFormatSpecifiers_ThenOutputChangesAccordingToCultur Assert.AreEqual($"8/24/2017{Environment.NewLine}" + $"24/8/2017{Environment.NewLine}" + - $"2017/08/24{Environment.NewLine}", sw.ToString()); + $"2017/08/24{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -49,7 +49,7 @@ public void WhenUsingDateTimeFormatInfo_ThenOutputChangesAccordingToCustomFormat Console.WriteLine(datetime.ToString("d", formatInfo)); // 8-24-2017 - Assert.AreEqual($"8-24-2017{Environment.NewLine}", sw.ToString()); + Assert.AreEqual($"8-24-2017{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -62,11 +62,12 @@ public void WhenUsingFormatSpecifierAndCulture_ThenCanParseCorrectString() } [TestMethod] - [ExpectedException(typeof(FormatException))] public void WhenUsingFormatSpecifierAndWrongCulture_ThenThrowsException() { var dateLiteral = "8/24/2017"; - DateTime.ParseExact(dateLiteral, "d", CultureInfo.CreateSpecificCulture("ja-JP")); + + Assert.ThrowsExactly(() => + DateTime.ParseExact(dateLiteral, "d", CultureInfo.CreateSpecificCulture("ja-JP"))); } [TestMethod] @@ -82,7 +83,7 @@ public void WhenUsingLongDateStandardFormat_ThenOutputChangesAccordingToCulture( Assert.AreEqual($"Thursday, August 24, 2017{Environment.NewLine}" + $"jueves, 24 de agosto de 2017{Environment.NewLine}" + - $"Donnerstag, 24. August 2017{Environment.NewLine}", sw.ToString()); + $"Donnerstag, 24. August 2017{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } } } diff --git a/dotnet-datetime/DateTimeFormatInCSharp/Test/StandardDateTimeFormatTests.cs b/dotnet-datetime/DateTimeFormatInCSharp/Test/StandardDateTimeFormatTests.cs index 4eced06826..61e747eae2 100644 --- a/dotnet-datetime/DateTimeFormatInCSharp/Test/StandardDateTimeFormatTests.cs +++ b/dotnet-datetime/DateTimeFormatInCSharp/Test/StandardDateTimeFormatTests.cs @@ -21,7 +21,7 @@ public void WhenUsingFullDateShortTimeStandardFormat_ThenOutputChangesAccordingT Assert.AreEqual($"Thursday, August 24, 2017 2:35 PM{Environment.NewLine}" + $"jueves, 24 de agosto de 2017 14:35{Environment.NewLine}" + - $"Donnerstag, 24. August 2017 14:35{Environment.NewLine}", sw.ToString()); + $"Donnerstag, 24. August 2017 14:35{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -37,7 +37,7 @@ public void WhenUsingFullDateLongTimeStandardFormat_ThenOutputChangesAccordingTo Assert.AreEqual($"Thursday, August 24, 2017 2:35:00 PM{Environment.NewLine}" + $"jueves, 24 de agosto de 2017 14:35:00{Environment.NewLine}" + - $"Donnerstag, 24. August 2017 14:35:00{Environment.NewLine}", sw.ToString()); + $"Donnerstag, 24. August 2017 14:35:00{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -53,7 +53,7 @@ public void WhenUsingGeneralDateShortTimeStandardFormat_ThenOutputChangesAccordi Assert.AreEqual($"8/24/2017 2:35 PM{Environment.NewLine}" + $"24/8/2017 14:35{Environment.NewLine}" + - $"24.08.2017 14:35{Environment.NewLine}", sw.ToString()); + $"24.08.2017 14:35{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -69,7 +69,7 @@ public void WhenUsingGeneralDateLongTimeStandardFormat_ThenOutputChangesAccordin Assert.AreEqual($"8/24/2017 2:35:00 PM{Environment.NewLine}" + $"24/8/2017 14:35:00{Environment.NewLine}" + - $"24.08.2017 14:35:00{Environment.NewLine}", sw.ToString()); + $"24.08.2017 14:35:00{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -85,7 +85,7 @@ public void WhenRoundTripStandardFormat_ThenTimeZoneIsPreserved() Console.WriteLine(dateTimeOffset.ToString("O")); // 2017-08-24T14:35:00.0000000+02:00 Assert.AreEqual($"2017-08-24T14:35:00.0000000Z{Environment.NewLine}" + - $"2017-08-24T14:35:00.0000000+02:00{Environment.NewLine}", sw.ToString()); + $"2017-08-24T14:35:00.0000000+02:00{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -94,14 +94,16 @@ public void WhenUsingRfc1123StandardFormat_ThenUtcDateTimeMustBeUsed() var sw = new StringWriter(); Console.SetOut(sw); - var datetime = new DateTime(2017, 8, 24, 14, 35, 0, DateTimeKind.Unspecified); + // The value is created as Utc so ToUniversalTime() is a no-op. An Unspecified value + // would be read as local time here, and the result would depend on the machine's zone. + var datetime = new DateTime(2017, 8, 24, 14, 35, 0, DateTimeKind.Utc); Console.WriteLine(datetime.ToUniversalTime().ToString("R")); // Thu, 24 Aug 2017 14:35:00 GMT var datetimeOffset = new DateTimeOffset(2017, 8, 24, 14, 35, 0, TimeSpan.FromHours(2)); Console.WriteLine(datetimeOffset.ToString("R")); // Thu, 24 Aug 2017 12:35:00 GMT Assert.AreEqual($"Thu, 24 Aug 2017 14:35:00 GMT{Environment.NewLine}" + - $"Thu, 24 Aug 2017 12:35:00 GMT{Environment.NewLine}", sw.ToString()); + $"Thu, 24 Aug 2017 12:35:00 GMT{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -113,7 +115,7 @@ public void WhenUsingSortableStandardFormat_ThenTimeZoneIsntPreserved() var datetime = new DateTime(2017, 8, 24, 14, 35, 0); Console.WriteLine(datetime.ToString("s")); // 2017-08-24T14:35:00 - Assert.AreEqual($"2017-08-24T14:35:00{Environment.NewLine}", sw.ToString()); + Assert.AreEqual($"2017-08-24T14:35:00{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -122,10 +124,12 @@ public void WhenUsingUniversalSortableStandardFormat_ThenUtcTimeIsRepresented() var sw = new StringWriter(); Console.SetOut(sw); - var datetime = new DateTime(2017, 8, 24, 14, 35, 0); + // Utc, for the same reason as the RFC 1123 test above: ToUniversalTime() on an + // Unspecified value converts from the machine's local zone. + var datetime = new DateTime(2017, 8, 24, 14, 35, 0, DateTimeKind.Utc); Console.WriteLine(datetime.ToUniversalTime().ToString("u")); // 2017-08-24 14:35:00Z - Assert.AreEqual($"2017-08-24 14:35:00Z{Environment.NewLine}", sw.ToString()); + Assert.AreEqual($"2017-08-24 14:35:00Z{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -139,7 +143,7 @@ public void WhenUsingUniversalFullFormatStandardFormat_ThenUtcTimeIsRepresented( Console.WriteLine(datetime.ToUniversalTime().ToString("U", CultureInfo.CreateSpecificCulture("es-ES"))); // jueves, 24 de agosto de 2017 14:35:00 Assert.AreEqual($"Thursday, August 24, 2017 2:35:00 PM{Environment.NewLine}" + - $"jueves, 24 de agosto de 2017 14:35:00{Environment.NewLine}", sw.ToString()); + $"jueves, 24 de agosto de 2017 14:35:00{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } } } diff --git a/dotnet-datetime/DateTimeFormatInCSharp/Test/StandardTimeFormatTests.cs b/dotnet-datetime/DateTimeFormatInCSharp/Test/StandardTimeFormatTests.cs index 787ee04e08..a5e343d05a 100644 --- a/dotnet-datetime/DateTimeFormatInCSharp/Test/StandardTimeFormatTests.cs +++ b/dotnet-datetime/DateTimeFormatInCSharp/Test/StandardTimeFormatTests.cs @@ -19,7 +19,7 @@ public void WhenUsingShortTimeStandardFormat_ThenTimeIsRepresented() Console.WriteLine(datetime.ToString("t", CultureInfo.CreateSpecificCulture("es-ES"))); // 14:35 Assert.AreEqual($"2:35 PM{Environment.NewLine}" + - $"14:35{Environment.NewLine}", sw.ToString()); + $"14:35{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } [TestMethod] @@ -33,7 +33,7 @@ public void WhenUsingLongTimeStandardFormat_ThenTimeIsRepresented() Console.WriteLine(datetime.ToString("T", CultureInfo.CreateSpecificCulture("es-ES"))); // 14:35:00 Assert.AreEqual($"2:35:00 PM{Environment.NewLine}" + - $"14:35:00{Environment.NewLine}", sw.ToString()); + $"14:35:00{Environment.NewLine}", TextExpectation.NormalizeSpaces(sw.ToString())); } } } diff --git a/dotnet-datetime/DateTimeFormatInCSharp/Test/Test.csproj b/dotnet-datetime/DateTimeFormatInCSharp/Test/Test.csproj index f72bee824b..7af97066cb 100644 --- a/dotnet-datetime/DateTimeFormatInCSharp/Test/Test.csproj +++ b/dotnet-datetime/DateTimeFormatInCSharp/Test/Test.csproj @@ -1,17 +1,17 @@ - net6.0 + net10.0 enable false - - - - + + + + diff --git a/dotnet-datetime/DateTimeFormatInCSharp/Test/TextExpectation.cs b/dotnet-datetime/DateTimeFormatInCSharp/Test/TextExpectation.cs new file mode 100644 index 0000000000..22e0980a2d --- /dev/null +++ b/dotnet-datetime/DateTimeFormatInCSharp/Test/TextExpectation.cs @@ -0,0 +1,22 @@ +using System.Globalization; +using System.Linq; + +namespace Test +{ + // The character a culture puts between the time and its AM/PM designator comes from the + // host's ICU data, not from the .NET version: en-US emits an ordinary space (U+0020) on + // some hosts and a narrow no-break space (U+202F) on others, so the same assertion can + // pass on a Windows desktop and fail in a Linux container. See dotnet/runtime#96022, + // closed "working as designed" ("culture formats are provided by the OS and change from + // time to time"). + // + // Every expectation in this project is therefore compared after normalising Unicode space + // separators to a plain space. The alternative -- pasting an invisible character into a + // string literal -- makes the expected value unreadable and still only works on one host. + internal static class TextExpectation + { + public static string NormalizeSpaces(string value) => + string.Concat(value.Select(character => + char.GetUnicodeCategory(character) == UnicodeCategory.SpaceSeparator ? ' ' : character)); + } +}