Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wasm] Use timezone abbreviations as fallback if full names don't exist #45385

Merged
merged 10 commits into from
Mar 5, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -1074,7 +1074,7 @@
<Compile Include="$(CommonPath)Interop\Interop.TimeZoneDisplayNameType.cs">
<Link>Common\Interop\Interop.TimeZoneDisplayNameType.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Interop.TimeZoneInfo.cs" Condition="'$(TargetsBrowser)' != 'true'">
<Compile Include="$(CommonPath)Interop\Interop.TimeZoneInfo.cs">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this changed needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we aren't using TimeZoneInfo.GetDisplayName.Invariant.cs anymore (will also remove that file in this PR) so Interop.Globalization is not defined for Browser

<Link>Common\Interop\Interop.TimeZoneInfo.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Interop.Utils.cs">
Expand Down Expand Up @@ -1865,7 +1865,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.Browser.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\DriveInfoInternal.Browser.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PersistedFiles.Browser.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.GetDisplayName.Invariant.cs" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete this file since it is no longer used? Also, TimeZoneInfo.GetDisplayName.cs can be merged back into TimeZoneInfo.GetDisplayName.Unix.cs.

<Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.GetDisplayName.cs" />
</ItemGroup>
<ItemGroup Condition="'$(IsOSXLike)' == 'true'">
<Compile Include="$(CommonPath)Interop\OSX\Interop.libobjc.cs">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public sealed partial class TimeZoneInfo
private const string TimeZoneEnvironmentVariable = "TZ";
private const string TimeZoneDirectoryEnvironmentVariable = "TZDIR";
private const string FallbackCultureName = "en-US";
private readonly string? _standardAbbrevName;
private readonly string? _daylightAbbrevName;

private TimeZoneInfo(byte[] data, string id, bool dstDisabled)
{
Expand Down Expand Up @@ -52,11 +54,11 @@ private TimeZoneInfo(byte[] data, string id, bool dstDisabled)
if (!transitionType[type].IsDst)
{
_baseUtcOffset = transitionType[type].UtcOffset;
_standardDisplayName = TZif_GetZoneAbbreviation(zoneAbbreviations, transitionType[type].AbbreviationIndex);
_standardAbbrevName = TZif_GetZoneAbbreviation(zoneAbbreviations, transitionType[type].AbbreviationIndex);
}
else
{
_daylightDisplayName = TZif_GetZoneAbbreviation(zoneAbbreviations, transitionType[type].AbbreviationIndex);
_daylightAbbrevName = TZif_GetZoneAbbreviation(zoneAbbreviations, transitionType[type].AbbreviationIndex);
}
}

Expand All @@ -69,21 +71,19 @@ private TimeZoneInfo(byte[] data, string id, bool dstDisabled)
if (!transitionType[i].IsDst)
{
_baseUtcOffset = transitionType[i].UtcOffset;
_standardDisplayName = TZif_GetZoneAbbreviation(zoneAbbreviations, transitionType[i].AbbreviationIndex);
_standardAbbrevName = TZif_GetZoneAbbreviation(zoneAbbreviations, transitionType[i].AbbreviationIndex);
}
else
{
_daylightDisplayName = TZif_GetZoneAbbreviation(zoneAbbreviations, transitionType[i].AbbreviationIndex);
_daylightAbbrevName = TZif_GetZoneAbbreviation(zoneAbbreviations, transitionType[i].AbbreviationIndex);
}
}
}
_displayName = _standardDisplayName;

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)
{
if (_baseUtcOffset >= TimeSpan.Zero)
Expand All @@ -92,6 +92,12 @@ private TimeZoneInfo(byte[] data, string id, bool dstDisabled)
_displayName = $"(UTC-{_baseUtcOffset:hh\\:mm}) {_standardDisplayName}";
}

if (string.IsNullOrEmpty(_daylightDisplayName) && !string.IsNullOrEmpty(_daylightAbbrevName))
_daylightDisplayName = _daylightAbbrevName;

if (string.IsNullOrEmpty(_standardDisplayName) && !string.IsNullOrEmpty(_standardAbbrevName))
_standardDisplayName = _standardAbbrevName;

// TZif supports seconds-level granularity with offsets but TimeZoneInfo only supports minutes since it aligns
// with DateTimeOffset, SQL Server, and the W3C XML Specification
if (_baseUtcOffset.Ticks % TimeSpan.TicksPerMinute != 0)
Expand Down Expand Up @@ -212,7 +218,6 @@ private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachine(string id, out
e = new InvalidTimeZoneException(SR.Format(SR.InvalidTimeZone_InvalidFileData, id, timeZoneFilePath));
return TimeZoneInfoResult.InvalidTimeZoneException;
}

return TimeZoneInfoResult.Success;
}

Expand Down Expand Up @@ -1481,7 +1486,6 @@ private static void TZif_ParseRaw(byte[] data, out TZifHead t, out DateTime[] dt
index += TZifHead.Length;
timeValuesLength = 8; // the second version uses 8-bytes
}

// initialize the containers for the rest of the TZ data
dts = new DateTime[t.TimeCount];
typeOfLocalTime = new byte[t.TimeCount];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1859,11 +1859,12 @@ private static TimeZoneInfoResult TryGetTimeZone(string id, bool dstDisabled, ou
private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachine(string id, bool dstDisabled, out TimeZoneInfo? value, out Exception? e, CachedData cachedData)
{
TimeZoneInfoResult result;

Internal.Console.Write($"ID {id} DST DISABLED {dstDisabled}\n");
result = TryGetTimeZoneFromLocalMachine(id, out TimeZoneInfo? match, out e);

if (result == TimeZoneInfoResult.Success)
{
Internal.Console.Write("TZ SUCCESS\n");
cachedData._systemTimeZones ??= new Dictionary<string, TimeZoneInfo>(StringComparer.OrdinalIgnoreCase)
{
{ UtcId, s_utcTimeZone }
Expand All @@ -1883,6 +1884,7 @@ private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachine(string id, bool
}
else
{
// Internal.Console.Write($"CREATE TZ WITH DST {match._daylightDisplayName!}");
value = new TimeZoneInfo(match!._id, match._baseUtcOffset, match._displayName, match._standardDisplayName,
match._daylightDisplayName, match._adjustmentRules, disableDaylightSavingTime: false);
}
Expand Down
53 changes: 51 additions & 2 deletions src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,54 @@ public static void Names()
Assert.NotNull(utc.ToString());
}

[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser))]
public static void BrowserDayLightNames()
{
// Due to ICU size limitations, full daylight names are not included.
// daylight name abbreviations, if available, are used instead.
TimeZoneInfo pacific = TimeZoneInfo.FindSystemTimeZoneById(s_strPacific);
Assert.Equal("PDT", pacific.DaylightName);
TimeZoneInfo sydney = TimeZoneInfo.FindSystemTimeZoneById(s_strSydney);
Assert.Equal("AEDT", sydney.DaylightName);
TimeZoneInfo perth = TimeZoneInfo.FindSystemTimeZoneById(s_strPerth);
Assert.Equal("AWDT", perth.DaylightName);
Assert.Equal("CEST", s_amsterdamTz.DaylightName);
TimeZoneInfo moscow = TimeZoneInfo.FindSystemTimeZoneById(s_strRussian);
Assert.Equal("MSD", moscow.DaylightName);
Assert.Equal("WEST", s_LisbonTz.DaylightName);
Assert.Equal("NDT", s_NewfoundlandTz.DaylightName);

// No IANA daylight name abbreviations available, default to
// UTC offset.
TimeZoneInfo iran = TimeZoneInfo.FindSystemTimeZoneById(s_strIran);
Assert.Equal("+0430", iran.DaylightName);
Assert.Equal("-02", s_catamarcaTz.DaylightName);
}

[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser))]
public static void BrowserStandardNames()
{
// Due to ICU size limitations, full standard names are not included.
// standard name abbreviations, if available, are used instead.
TimeZoneInfo pacific = TimeZoneInfo.FindSystemTimeZoneById(s_strPacific);
Assert.Equal("PST", pacific.StandardName);
TimeZoneInfo sydney = TimeZoneInfo.FindSystemTimeZoneById(s_strSydney);
Assert.Equal("AEST", sydney.StandardName);
TimeZoneInfo perth = TimeZoneInfo.FindSystemTimeZoneById(s_strPerth);
Assert.Equal("AWST", perth.StandardName);
Assert.Equal("CET", s_amsterdamTz.StandardName);
TimeZoneInfo moscow = TimeZoneInfo.FindSystemTimeZoneById(s_strRussian);
Assert.Equal("MSK", moscow.StandardName);
Assert.Equal("WET", s_LisbonTz.StandardName);
Assert.Equal("NST", s_NewfoundlandTz.StandardName);

// No IANA standard name abbreviations available, default to
// UTC offset.
TimeZoneInfo iran = TimeZoneInfo.FindSystemTimeZoneById(s_strIran);
Assert.Equal("-03", s_catamarcaTz.StandardName);
Assert.Equal("+0330", iran.StandardName);
}

[Fact]
public static void ConvertTime()
{
Expand Down Expand Up @@ -2269,15 +2317,16 @@ public static void TimeZoneInfo_DisplayNameStartsWithOffset()
if (tzi.Id != "UTC")
{
Assert.False(string.IsNullOrWhiteSpace(tzi.StandardName));
Assert.Matches(@"^\(UTC(\+|-)[0-9]{2}:[0-9]{2}\) \S.*", tzi.DisplayName);

Console.WriteLine($"DISPLAY NAME {tzi.DisplayName}");
Assert.Matches(@"^\(UTC(\+|-)[0-9]{2}:[0-9]{2}\)( \S.*)*", tzi.DisplayName);
// see https://github.com/dotnet/corefx/pull/33204#issuecomment-438782500
if (PlatformDetection.IsNotWindowsNanoServer && !PlatformDetection.IsWindows7)
{
string offset = Regex.Match(tzi.DisplayName, @"(-|)[0-9]{2}:[0-9]{2}").Value;
TimeSpan ts = TimeSpan.Parse(offset);
if (tzi.BaseUtcOffset != ts && tzi.Id.IndexOf("Morocco", StringComparison.Ordinal) >= 0)
{
Console.WriteLine(tzi.BaseUtcOffset);
// Windows data can report display name with UTC+01:00 offset which is not matching the actual BaseUtcOffset.
// We special case this in the test to avoid the test failures like:
// 01:00 != 00:00:00, dn:(UTC+01:00) Casablanca, sn:Morocco Standard Time
Expand Down