Skip to content

Commit

Permalink
[wasm] If standard, or daylight names are not available, then fallback
Browse files Browse the repository at this point in the history
.. to abbreviations.

[This code](https://github.com/dotnet/runtime/blob/master/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.GetDisplayName.cs#L29-L40) seems to return `true` result, even though `timeZoneDisplayName` is null/empty. So, in such a case don't set the out var
and let the abbrev get used as the fallback (like it already says in the
comment).
  • Loading branch information
radical committed Jan 13, 2021
1 parent 33ff298 commit 1805c6d
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 78 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ private unsafe void GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType

// 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)
if (result && !string.IsNullOrEmpty(timeZoneDisplayName))
{
displayName = timeZoneDisplayName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ 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 All @@ -36,6 +34,8 @@ private TimeZoneInfo(byte[] data, string id, bool dstDisabled)
bool[] StandardTime;
bool[] GmtTime;
string? futureTransitionsPosixFormat;
string? standardAbbrevName = null;
string? daylightAbbrevName = null;

// parse the raw TZif bytes; this method can throw ArgumentException when the data is malformed.
TZif_ParseRaw(data, out t, out dts, out typeOfLocalTime, out transitionType, out zoneAbbreviations, out StandardTime, out GmtTime, out futureTransitionsPosixFormat);
Expand All @@ -54,11 +54,11 @@ private TimeZoneInfo(byte[] data, string id, bool dstDisabled)
if (!transitionType[type].IsDst)
{
_baseUtcOffset = transitionType[type].UtcOffset;
_standardAbbrevName = TZif_GetZoneAbbreviation(zoneAbbreviations, transitionType[type].AbbreviationIndex);
standardAbbrevName = TZif_GetZoneAbbreviation(zoneAbbreviations, transitionType[type].AbbreviationIndex);
}
else
{
_daylightAbbrevName = TZif_GetZoneAbbreviation(zoneAbbreviations, transitionType[type].AbbreviationIndex);
daylightAbbrevName = TZif_GetZoneAbbreviation(zoneAbbreviations, transitionType[type].AbbreviationIndex);
}
}

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

// Use abbrev as the fallback
_standardDisplayName = standardAbbrevName;
_daylightDisplayName = daylightAbbrevName;
_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,12 +98,6 @@ 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 @@ -218,6 +218,7 @@ 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 @@ -1486,6 +1487,7 @@ 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,12 +1859,11 @@ 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 @@ -1884,7 +1883,6 @@ 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
71 changes: 22 additions & 49 deletions src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,54 +81,28 @@ 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);
// Due to ICU size limitations, full daylight/standard names are not included.
// name abbreviations, if available, are used instead
public static TheoryData<TimeZoneInfo, string, string, string> GetBrowser_TimeZoneNamesTestData()
{
return new TheoryData<TimeZoneInfo, string, string, string>
{
{ TimeZoneInfo.FindSystemTimeZoneById(s_strPacific), "(UTC-08:00) PST", "PST", "PDT" },
{ TimeZoneInfo.FindSystemTimeZoneById(s_strSydney), "(UTC+10:00) AEST", "AEST", "AEDT" },
{ TimeZoneInfo.FindSystemTimeZoneById(s_strPerth), "(UTC+08:00) AWST", "AWST", "AWDT" },
{ TimeZoneInfo.FindSystemTimeZoneById(s_strIran), "(UTC+03:30) +0330", "+0330", "+0430" },

{ s_NewfoundlandTz, "(UTC-03:30) NST", "NST", "NDT" },
{ s_catamarcaTz, "(UTC-03:00) -03", "-03", "-02" }
};
}

[ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser))]
[MemberData(nameof(GetBrowser_TimeZoneNamesTestData))]
public static void Browser_TimeZoneNames(TimeZoneInfo tzi, string displayName, string standardName, string daylightName)
=> Assert.Equal($"DisplayName: {tzi.DisplayName}, StandardName: {tzi.StandardName}, DaylightName: {tzi.DaylightName}",
$"DisplayName: {displayName}, StandardName: {standardName}, DaylightName: {daylightName}");

[Fact]
public static void ConvertTime()
{
Expand Down Expand Up @@ -2317,16 +2291,15 @@ public static void TimeZoneInfo_DisplayNameStartsWithOffset()
if (tzi.Id != "UTC")
{
Assert.False(string.IsNullOrWhiteSpace(tzi.StandardName));
Console.WriteLine($"DISPLAY NAME {tzi.DisplayName}");
Assert.Matches(@"^\(UTC(\+|-)[0-9]{2}:[0-9]{2}\)( \S.*)*", 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

0 comments on commit 1805c6d

Please sign in to comment.