Skip to content

Commit 88cc71b

Browse files
authored
Fix getting time zone names with Invariant Culture (#33318)
* Fix getting time zone names with Invariant Culture * Address the feedback * Restrict the test to English languages only. * Fix misspelling * Remove un-needed line
1 parent a21da8f commit 88cc71b

File tree

2 files changed

+48
-5
lines changed

2 files changed

+48
-5
lines changed

src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public sealed partial class TimeZoneInfo
2424
private const string ZoneTabFileName = "zone.tab";
2525
private const string TimeZoneEnvironmentVariable = "TZ";
2626
private const string TimeZoneDirectoryEnvironmentVariable = "TZDIR";
27+
private const string FallbackCultureName = "en-US";
2728

2829
private TimeZoneInfo(byte[] data, string id, bool dstDisabled)
2930
{
@@ -80,9 +81,10 @@ private TimeZoneInfo(byte[] data, string id, bool dstDisabled)
8081
}
8182
_displayName = _standardDisplayName;
8283

83-
GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType.Generic, ref _displayName);
84-
GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType.Standard, ref _standardDisplayName);
85-
GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType.DaylightSavings, ref _daylightDisplayName);
84+
string uiCulture = CultureInfo.CurrentUICulture.Name.Length == 0 ? FallbackCultureName : CultureInfo.CurrentUICulture.Name; // ICU doesn't work nicely with Invariant
85+
GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType.Generic, uiCulture, ref _displayName);
86+
GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType.Standard, uiCulture, ref _standardDisplayName);
87+
GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType.DaylightSavings, uiCulture, ref _daylightDisplayName);
8688

8789
if (_standardDisplayName == _displayName)
8890
{
@@ -108,7 +110,7 @@ private TimeZoneInfo(byte[] data, string id, bool dstDisabled)
108110
ValidateTimeZoneInfo(_id, _baseUtcOffset, _adjustmentRules, out _supportsDaylightSavingTime);
109111
}
110112

111-
private unsafe void GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType nameType, ref string? displayName)
113+
private unsafe void GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType nameType, string uiCulture, ref string? displayName)
112114
{
113115
if (GlobalizationMode.Invariant)
114116
{
@@ -125,11 +127,28 @@ private unsafe void GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType
125127
return Interop.Globalization.GetTimeZoneDisplayName(locale, id, type, bufferPtr, buffer.Length);
126128
}
127129
},
128-
CultureInfo.CurrentUICulture.Name,
130+
uiCulture,
129131
_id,
130132
nameType,
131133
out timeZoneDisplayName);
132134

135+
if (!result && uiCulture != FallbackCultureName)
136+
{
137+
// Try to fallback using FallbackCultureName just in case we can make it work.
138+
result = Interop.CallStringMethod(
139+
(buffer, locale, id, type) =>
140+
{
141+
fixed (char* bufferPtr = buffer)
142+
{
143+
return Interop.Globalization.GetTimeZoneDisplayName(locale, id, type, bufferPtr, buffer.Length);
144+
}
145+
},
146+
FallbackCultureName,
147+
_id,
148+
nameType,
149+
out timeZoneDisplayName);
150+
}
151+
133152
// If there is an unknown error, don't set the displayName field.
134153
// It will be set to the abbreviation that was read out of the tzfile.
135154
if (result)

src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System.Runtime.InteropServices;
1111
using System.Runtime.Serialization.Formatters.Binary;
1212
using System.Text.RegularExpressions;
13+
using Microsoft.DotNet.RemoteExecutor;
1314
using Xunit;
1415

1516
namespace System.Tests
@@ -2268,6 +2269,29 @@ public static void EnsureUtcObjectSingleton()
22682269
Assert.True(ReferenceEquals(TimeZoneInfo.FindSystemTimeZoneById("UTC"), TimeZoneInfo.Utc));
22692270
}
22702271

2272+
// We test the existance of a specific English time zone name to avoid failures on non-English platforms.
2273+
[ConditionalFact(nameof(IsEnglishUILanguage))]
2274+
public static void TestNameWithInvariantCulture()
2275+
{
2276+
RemoteExecutor.Invoke(() =>
2277+
{
2278+
// 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.
2279+
// We fixed this issue by avoid sending the invariant culture name to ICU and this test is confirming we work fine at that time.
2280+
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
2281+
TimeZoneInfo.ClearCachedData();
2282+
2283+
TimeZoneInfo pacific = TimeZoneInfo.FindSystemTimeZoneById(s_strPacific);
2284+
2285+
Assert.True(pacific.StandardName.IndexOf("Pacific", StringComparison.OrdinalIgnoreCase) >= 0, $"'{pacific.StandardName}' is not the expected standard name for Pacific time zone");
2286+
Assert.True(pacific.DaylightName.IndexOf("Pacific", StringComparison.OrdinalIgnoreCase) >= 0, $"'{pacific.DaylightName}' is not the expected daylight name for Pacific time zone");
2287+
Assert.True(pacific.DisplayName.IndexOf("Pacific", StringComparison.OrdinalIgnoreCase) >= 0, $"'{pacific.DisplayName}' is not the expected display name for Pacific time zone");
2288+
2289+
}).Dispose();
2290+
2291+
}
2292+
2293+
private static bool IsEnglishUILanguage() => CultureInfo.CurrentUICulture.Name == "en" || CultureInfo.CurrentUICulture.Name.StartsWith("en-", StringComparison.Ordinal);
2294+
22712295
private static void VerifyConvertException<TException>(DateTimeOffset inputTime, string destinationTimeZoneId) where TException : Exception
22722296
{
22732297
Assert.ThrowsAny<TException>(() => TimeZoneInfo.ConvertTime(inputTime, TimeZoneInfo.FindSystemTimeZoneById(destinationTimeZoneId)));

0 commit comments

Comments
 (0)