diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs index 3a5ecca2009996..92d3130a6c764d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs @@ -24,6 +24,7 @@ public sealed partial class TimeZoneInfo private const string ZoneTabFileName = "zone.tab"; private const string TimeZoneEnvironmentVariable = "TZ"; private const string TimeZoneDirectoryEnvironmentVariable = "TZDIR"; + private const string FallbackCultureName = "en-US"; private TimeZoneInfo(byte[] data, string id, bool dstDisabled) { @@ -80,9 +81,10 @@ private TimeZoneInfo(byte[] data, string id, bool dstDisabled) } _displayName = _standardDisplayName; - GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType.Generic, ref _displayName); - GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType.Standard, ref _standardDisplayName); - GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType.DaylightSavings, ref _daylightDisplayName); + string uiCulture = CultureInfo.CurrentUICulture.Name.Length == 0 ? FallbackCultureName : CultureInfo.CurrentUICulture.Name; // ICU doesn't work nicely with Invariant + GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType.Generic, uiCulture, ref _displayName); + GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType.Standard, uiCulture, ref _standardDisplayName); + GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType.DaylightSavings, uiCulture, ref _daylightDisplayName); if (_standardDisplayName == _displayName) { @@ -108,7 +110,7 @@ private TimeZoneInfo(byte[] data, string id, bool dstDisabled) ValidateTimeZoneInfo(_id, _baseUtcOffset, _adjustmentRules, out _supportsDaylightSavingTime); } - private unsafe void GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType nameType, ref string? displayName) + private unsafe void GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType nameType, string uiCulture, ref string? displayName) { if (GlobalizationMode.Invariant) { @@ -125,11 +127,28 @@ private unsafe void GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType return Interop.Globalization.GetTimeZoneDisplayName(locale, id, type, bufferPtr, buffer.Length); } }, - CultureInfo.CurrentUICulture.Name, + uiCulture, _id, nameType, out timeZoneDisplayName); + if (!result && uiCulture != FallbackCultureName) + { + // Try to fallback using FallbackCultureName just in case we can make it work. + result = Interop.CallStringMethod( + (buffer, locale, id, type) => + { + fixed (char* bufferPtr = buffer) + { + return Interop.Globalization.GetTimeZoneDisplayName(locale, id, type, bufferPtr, buffer.Length); + } + }, + FallbackCultureName, + _id, + nameType, + out timeZoneDisplayName); + } + // If there is an unknown error, don't set the displayName field. // It will be set to the abbreviation that was read out of the tzfile. if (result) diff --git a/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs b/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs index 5ff7f8e905cbd1..8738d64022ce69 100644 --- a/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs +++ b/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs @@ -10,6 +10,7 @@ using System.Runtime.InteropServices; using System.Runtime.Serialization.Formatters.Binary; using System.Text.RegularExpressions; +using Microsoft.DotNet.RemoteExecutor; using Xunit; namespace System.Tests @@ -2268,6 +2269,29 @@ public static void EnsureUtcObjectSingleton() Assert.True(ReferenceEquals(TimeZoneInfo.FindSystemTimeZoneById("UTC"), TimeZoneInfo.Utc)); } + // We test the existance of a specific English time zone name to avoid failures on non-English platforms. + [ConditionalFact(nameof(IsEnglishUILanguage))] + public static void TestNameWithInvariantCulture() + { + RemoteExecutor.Invoke(() => + { + // We call ICU to get the names. When passing invariant culture name to ICU, it fail and we'll use the abbreviated names at that time. + // We fixed this issue by avoid sending the invariant culture name to ICU and this test is confirming we work fine at that time. + CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture; + TimeZoneInfo.ClearCachedData(); + + TimeZoneInfo pacific = TimeZoneInfo.FindSystemTimeZoneById(s_strPacific); + + Assert.True(pacific.StandardName.IndexOf("Pacific", StringComparison.OrdinalIgnoreCase) >= 0, $"'{pacific.StandardName}' is not the expected standard name for Pacific time zone"); + Assert.True(pacific.DaylightName.IndexOf("Pacific", StringComparison.OrdinalIgnoreCase) >= 0, $"'{pacific.DaylightName}' is not the expected daylight name for Pacific time zone"); + Assert.True(pacific.DisplayName.IndexOf("Pacific", StringComparison.OrdinalIgnoreCase) >= 0, $"'{pacific.DisplayName}' is not the expected display name for Pacific time zone"); + + }).Dispose(); + + } + + private static bool IsEnglishUILanguage() => CultureInfo.CurrentUICulture.Name == "en" || CultureInfo.CurrentUICulture.Name.StartsWith("en-", StringComparison.Ordinal); + private static void VerifyConvertException(DateTimeOffset inputTime, string destinationTimeZoneId) where TException : Exception { Assert.ThrowsAny(() => TimeZoneInfo.ConvertTime(inputTime, TimeZoneInfo.FindSystemTimeZoneById(destinationTimeZoneId)));