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

IANA To/From Windows Ids Conversion APIs #51093

Merged
merged 3 commits into from
Apr 14, 2021

Conversation

tarekgh
Copy link
Member

@tarekgh tarekgh commented Apr 12, 2021

Fixes #49407

@dotnet-issue-labeler
Copy link

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

@ghost
Copy link

ghost commented Apr 12, 2021

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

Issue Details

Fixes #49407

Author: tarekgh
Assignees: -
Labels:

area-System.Globalization, new-api-needs-documentation

Milestone: -

@tarekgh tarekgh added this to the 6.0.0 milestone Apr 12, 2021
@tarekgh tarekgh self-assigned this Apr 12, 2021
@tarekgh
Copy link
Member Author

tarekgh commented Apr 12, 2021

@tarekgh
Copy link
Member Author

tarekgh commented Apr 12, 2021

@marek-safar @eerhardt @lewing could you please advise what is wrong here?
https://dev.azure.com/dnceng/public/_build/results?buildId=1082685&view=logs&jobId=f217f715-90ad-5404-53bd-b93e3b80f465&j=f217f715-90ad-5404-53bd-b93e3b80f465&t=d9b45970-da80-576b-c4ba-5d7bc638e9e8

This is browser/AOT/Mono error. do we need to add something to Mono's runtime or should I exclude it?

@filipnavara
Copy link
Member

Browser configurations do not contain the time zone data and hence the Interop file is also excluded:

<Compile Include="$(CommonPath)Interop\Interop.TimeZoneInfo.cs" Condition="'$(TargetsBrowser)' != 'true'" >
<Link>Common\Interop\Interop.TimeZoneInfo.cs</Link>
</Compile>

This was already mentioned in one of the issues related to time zone names.

@eerhardt
Copy link
Member

@marek-safar @eerhardt @lewing could you please advise what is wrong here?
https://dev.azure.com/dnceng/public/_build/results?buildId=1082685&view=logs&jobId=f217f715-90ad-5404-53bd-b93e3b80f465&j=f217f715-90ad-5404-53bd-b93e3b80f465&t=d9b45970-da80-576b-c4ba-5d7bc638e9e8

This is browser/AOT/Mono error. do we need to add something to Mono's runtime or should I exclude it?

See

<Compile Include="$(CommonPath)Interop\Interop.TimeZoneInfo.cs" Condition="'$(TargetsBrowser)' != 'true'" >
<Link>Common\Interop\Interop.TimeZoneInfo.cs</Link>
</Compile>

This falls into the same category as #50210. We need to make sure we aren't regressing size on Blazor WASM when we are making new calls to ICU.

@tarekgh
Copy link
Member Author

tarekgh commented Apr 12, 2021

@lewing @eerhardt

This falls into the same category as #50210. We need to make sure we aren't regressing size on Blazor WASM when we are making new calls to ICU.

Thanks for the reply. I want to confirm, are we ok excluding this new functionality for browser? these new APIs wouldn't work at all.

@lewing
Copy link
Member

lewing commented Apr 12, 2021

@lewing @eerhardt

This falls into the same category as #50210. We need to make sure we aren't regressing size on Blazor WASM when we are making new calls to ICU.

Thanks for the reply. I want to confirm, are we ok excluding this new functionality for browser? these new APIs wouldn't work at all.

We'd be happy to support this optionally but the size impact is large enough that it we probably can't do it by default. Just referencing the symbols pulled in 40k of compressed runtime size in #50210 and @tqiu8 can weigh in with the actual zone data sizes but if memory serves even just "en" was huge.

@tarekgh
Copy link
Member Author

tarekgh commented Apr 12, 2021

@eerhardt I have addressed the feedback. do you have any more comments or we are good to go?

@mattjohnsonpint
Copy link
Contributor

@lewing - not sure exactly what changes would be required for packaging, but this feature would only need the windowsZones file.

@mattjohnsonpint
Copy link
Contributor

Oh wait - it would also need to be able to resolve aliases. In CLDR, that's done in timezone.xml. I think that might be part of the zoneinfo64 data file in ICU.

Copy link
Member

@eerhardt eerhardt left a comment

Choose a reason for hiding this comment

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

:shipit:

Copy link
Member

@safern safern left a comment

Choose a reason for hiding this comment

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

LGTM

@tarekgh tarekgh merged commit defd712 into dotnet:main Apr 14, 2021
@tarekgh tarekgh deleted the IanaIdsConversionAPIs branch April 14, 2021 04:08
GetDisplayName(UtcId, Interop.Globalization.TimeZoneDisplayNameType.Standard, uiCulture.Name, ref standardDisplayName);

// Final safety check. Don't allow null or abbreviations
if (standardDisplayName == null || standardDisplayName == "GMT" || standardDisplayName == "UTC")
Copy link
Contributor

Choose a reason for hiding this comment

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

I see in some place such culture sensitive string comparisons. Make sense to use ordinal?

(Below I see if (windowsId.Equals("utc", StringComparison.OrdinalIgnoreCase)))

{
// Try to fallback using FallbackCultureName just in case we can make it work.
result = Interop.CallStringMethod(
(buffer, locale, id, type) =>
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we set static?

Suggested change
(buffer, locale, id, type) =>
static (buffer, locale, id, type) =>


string? timeZoneDisplayName;
bool result = Interop.CallStringMethod(
(buffer, locale, id, type) =>
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we set static?

Suggested change
(buffer, locale, id, type) =>
static (buffer, locale, id, type) =>

}

// Helper function that retrieves various forms of time zone display names from ICU
private static unsafe void GetDisplayName(string timeZoneId, Interop.Globalization.TimeZoneDisplayNameType nameType, string uiCulture, ref string? displayName)
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder why we use ref string? displayName. Can it be "out":

Suggested change
private static unsafe void GetDisplayName(string timeZoneId, Interop.Globalization.TimeZoneDisplayNameType nameType, string uiCulture, ref string? displayName)
private static unsafe void GetDisplayName(string timeZoneId, Interop.Globalization.TimeZoneDisplayNameType nameType, string uiCulture, out string? displayName)


// See if we should include the exemplar city name.
string exemplarCityName = GetExemplarCityName(timeZoneId, uiCulture.Name);
if (uiCulture.CompareInfo.IndexOf(genericName, exemplarCityName, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) >= 0 && genericLocationName != null)
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe swap conditions:

Suggested change
if (uiCulture.CompareInfo.IndexOf(genericName, exemplarCityName, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) >= 0 && genericLocationName != null)
if (genericLocationName != null && uiCulture.CompareInfo.IndexOf(genericName, exemplarCityName, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) >= 0)

}

windowsId = null;
Copy link
Contributor

Choose a reason for hiding this comment

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

All such initializations from all code paths in the method could be removed and one could be added on top of the method.

}

windowsId = null;
return false;
Copy link
Contributor

Choose a reason for hiding this comment

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

To continue previous comment we could make the code more readable:

Suggested change
return false;
if (allocate && length > 0)
{
windowsId = new string(buffer, 0, length);
}
return length > 0;

Comment on lines -185 to +77
{
// When an exemplar city is already part of the generic name,
// there's no need to repeat it again so just use the generic name.

// *** Example (fr-FR) ***
// id = "Australia/Lord_Howe"
// baseOffsetText = "(UTC+10:30)"
// standardName = "heure normale de Lord Howe"
// genericName = "heure de Lord Howe"
// genericLocationName = "heure : Lord Howe"
// exemplarCityName = "Lord Howe"
// displayName = "(UTC+10:30) heure de Lord Howe"

displayName = $"{baseOffsetText} {genericName}";
}
else
char* buffer = stackalloc char[100];
int length = Interop.Globalization.WindowsIdToIanaId(windowsId, region, buffer, 100);
if (length > 0)
{
// Finally, use the generic name and the exemplar city together.
// This provides an intuitive name and still disambiguates.

// *** Example (en-US) ***
// id = "Europe/Rome"
// baseOffsetText = "(UTC+01:00)"
// standardName = "Central European Standard Time"
// genericName = "Central European Time"
// genericLocationName = "Italy Time"
// exemplarCityName = "Rome"
// displayName = "(UTC+01:00) Central European Time (Rome)"

displayName = $"{baseOffsetText} {genericName} ({exemplarCityName})";
ianaId = allocate ? new string(buffer, 0, length) : null;
return true;
}
}

// Helper function that gets an exmplar city name either from ICU or from the IANA time zone ID itself
private static string GetExemplarCityName(string timeZoneId, string uiCultureName)
{
// First try to get the name through the localization data.
string? exemplarCityName = null;
GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.ExemplarCity, uiCultureName, ref exemplarCityName);
if (!string.IsNullOrEmpty(exemplarCityName))
return exemplarCityName;

// Support for getting exemplar city names was added in ICU 51.
// We may have an older version. For example, in Helix we test on RHEL 7.5 which uses ICU 50.1.2.
// We'll fallback to using an English name generated from the time zone ID.
int i = timeZoneId.LastIndexOf('/');
return timeZoneId.Substring(i + 1).Replace('_', ' ');
ianaId = null;
return false;
Copy link
Contributor

Choose a reason for hiding this comment

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

The same. ianaId = null could be on top of the method and:

            if (allocate && length > 0)
            {
                ianaId = new string(buffer, 0, length);
            }

            return length > 0;

@iSazonov
Copy link
Contributor

Oops! ^-) Already merged.

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 IANA Ids to/From Windows Ids conversion APIs
10 participants