Skip to content

Commit

Permalink
Fix getting time zone names with Invariant Culture (dotnet#33318)
Browse files Browse the repository at this point in the history
* Fix getting time zone names with Invariant Culture

* Address the feedback

* Restrict the test to English languages only.

* Fix misspelling

* Remove un-needed line
  • Loading branch information
tarekgh authored Mar 10, 2020
1 parent a21da8f commit 88cc71b
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
{
Expand All @@ -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)
{
Expand All @@ -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)
Expand Down
24 changes: 24 additions & 0 deletions src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<TException>(DateTimeOffset inputTime, string destinationTimeZoneId) where TException : Exception
{
Assert.ThrowsAny<TException>(() => TimeZoneInfo.ConvertTime(inputTime, TimeZoneInfo.FindSystemTimeZoneById(destinationTimeZoneId)));
Expand Down

0 comments on commit 88cc71b

Please sign in to comment.