Skip to content

Allow TimeZoneInfo display names to use any of the installed Windows languages - #52992

Merged
tarekgh merged 10 commits into
dotnet:mainfrom
mattjohnsonpint:tz-loc-win
May 21, 2021
Merged

tarekgh merged 10 commits into
dotnet:mainfrom
mattjohnsonpint:tz-loc-win

Conversation

@mattjohnsonpint

@mattjohnsonpint mattjohnsonpint commented May 19, 2021 •

Copy link
Copy Markdown
Contributor

TimeZoneInfo on Windows previously was locked to using only the OS default UI language for its DisplayName, StandardName and DaylightName properties (contrast with Unix where it will use the CultureInfo.CurrentUICulture).

Since the data comes from the NLS resources installed with Windows language packs, we still can't support all languages on Windows. However, with this change we can now support any that are installed. If the CurrentUICulture matches one of the languages installed (e.g. fr-FR = fr-FR) it will use it. Otherwise, it will search for an installed language with the same parent language (e.g. fr-CA ≈ fr-FR) to use. Finally, it will fallback to the existing behavior of using the default language.

Note, the unit test will only run if there are at least two languages installed that have different root languages, as we need to compare that lang1's values are different than lang2's. I'm not sure if there are any machines in Helix set up like this, but the test does work on my local machine with English and French installed.

Fixes #50310

@tarekgh

@ghost ghost added the area-System.Runtime label May 19, 2021
@ghost

ghost commented May 19, 2021

Copy link
Copy Markdown

Tagging subscribers to this area: @tannergooding
See info in area-owners.md if you want to be subscribed.

Issue Details

TimeZoneInfo on Windows previously was locked to using only the OS default UI language for its DisplayName, StandardName and DaylightName properties (contrast with Unix where it will use the CultureInfo.CurrentUICulture).

Since the data comes from the NLS resources installed with Windows language packs, we still can't support all languages on Windows. However, with this change we can now support any that are installed. If the CurrentUICulture matches one of the languages installed (e.g. fr-FR = fr-FR) it will use it. Otherwise, it will search for an installed language with the same parent language (e.g. fr-CA ≈ fr-FR) to use. Finally, it will fallback to the existing behavior of using the default language.

Note, the unit test will only run if there are at least two language packs installed that have different root languages, as we need to compare that lang1's values are different than lang2's. I'm not sure if there are any machines in Helix set up like this, but the test does work on my local machine with English and French installed.

Fixes #50310

@tarekgh

Author: mattjohnsonpint
Assignees: -
Labels:

area-System.Runtime

Milestone: -

Comment thread src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs Outdated
Comment thread src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs Outdated
Comment thread src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs Outdated
Comment thread src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs Outdated
Comment thread src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs Outdated
Comment thread src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs Outdated
Comment thread src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs Outdated
Comment thread src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs Outdated
Comment thread src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs Outdated
Comment thread src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs Outdated
@tarekgh

tarekgh commented May 20, 2021

Copy link
Copy Markdown
Member
src\libraries\System.Private.CoreLib\src\System\TimeZoneInfo.cs#L1935
src\libraries\System.Private.CoreLib\src\System\TimeZoneInfo.cs(1935,22): error CS7036: (NETCORE_ENGINEERING_TELEMETRY=Build) There is no argument given that corresponds to the required formal parameter 'e' of 'TimeZoneInfo.TryGetTimeZoneFromLocalMachine(string, bool, out TimeZoneInfo?, out Exception?, TimeZoneInfo.CachedData)'

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

@tarekgh tarekgh left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks @mattjohnsonpint. LGTM

@tarekgh tarekgh added this to the 6.0.0 milestone May 20, 2021
Comment thread src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs Outdated
@mattjohnsonpint

Copy link
Copy Markdown
Contributor Author

Remaining failures appear unrelated.

@tarekgh

tarekgh commented May 21, 2021

Copy link
Copy Markdown
Member

@mattjohnsonpint I have restarted the failed tests.

@tarekgh

tarekgh commented May 21, 2021

Copy link
Copy Markdown
Member

I opened the issue #53110 to track the unrelated failure in the CI leg runtime (Libraries Test Run checked coreclr windows x86 Release).

@tarekgh
tarekgh merged commit b54dbaf into dotnet:main May 21, 2021
@jkotas

jkotas commented May 21, 2021

Copy link
Copy Markdown
Member

I opened the issue #53110 to track the unrelated failure in the CI leg

@tarekgh This failure was introduced by this PR. It is caused by interop definitions in WindowsUILanguageHelper.

You may argue that the runtime should not assert and I agree, but that is not good enough justification for merging a change that will cause every PR to be red.

@mattjohnsonpint

Copy link
Copy Markdown
Contributor Author

@tarekgh This failure was introduced by this PR. It is caused by interop definitions in WindowsUILanguageHelper.

Can you please explain further? I will fix asap.

Comment thread src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs
@tarekgh

tarekgh commented May 21, 2021 •

Copy link
Copy Markdown
Member

Sorry I overlooked the failure. @mattjohnsonpint is it ok to revert the changes and resubmit it with the fix?

private const string InvariantUtcStandardDisplayName = "Coordinated Universal Time";

private static readonly Dictionary<string, string> s_FileMuiPathCache = new();
private static readonly TimeZoneInfo s_utcTimeZone = CreateUtcTimeZone();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We have a TimeZoneInfo.cs file. This private static readonly TimeZoneInfo s_utcTimeZone = CreateUtcTimeZone(); is now declared in both TimeZoneInfo.Unix.cs and TimeZoneInfo.Win32.cs. Can this not be deduplicated into the shared TimeZoneInfo.cs file?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This one was duplicated intentionally, as the mui cache has to be initialized first, and there's no sequence guaranteed for static initialization across partial class files. The alternative was to initialize the mui cache in the main file with a #if TARGET_WINDOWS around it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This one was duplicated intentionally, as the mui cache has to be initialized first, and there's no sequence guaranteed for static initialization across partial class files.

Ok... then it's missing a comment explaining that ;-)

private const int MaxKeyLength = 255;
private const string InvariantUtcStandardDisplayName = "Coordinated Universal Time";

private static readonly Dictionary<string, string> s_FileMuiPathCache = new();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: s_fileMuiPathCache

Comment on lines +818 to +819
var lang = new string(language);
if (string.Equals(lang, cultureInfo.Name, StringComparison.OrdinalIgnoreCase))

@stephentoub stephentoub May 25, 2021 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We shouldn't need to allocate the string just to do this comparison. Presumably exact match is common and we'd want to delay the creation of lang until after this if block? This can instead be:

ReadOnlySpan<char> lang = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(language);
if (lang.Equals(cultureInfo.Name, StringComparison.OrdinalIgnoreCase)

Then below if we need lang for the GetCultureInfo call, that can just be CultureInfo.GetCultureInfo(lang.ToString());.


if (succeeded)
{
fileMuiPath[Interop.Kernel32.MAX_PATH] = '\0';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This line exists in multiple places here and below. It would be safer and simpler to just dedup that into a single line above just after the GetFileMUIPath call.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not sure if it's even needed. The API always returns a null terminated string. Tarek asked for it just to be safe. Note, the two length values from the API are useless - they are the buffer lengths, not the values lengths.

return new string(fileMuiPath);
}

// Shouldn't get here, as there's always at least one language installed.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This would ideally be a Debug.Fail.

@ghost ghost locked as resolved and limited conversation to collaborators Jun 24, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Time zone display names are locked to the default display language on Windows

5 participants