[tz] Timezone selectors (was RES: Timezone for Brazil)

Matt Johnson-Pint mattjohnsonpint at gmail.com
Thu Oct 12 17:24:23 UTC 2023


Re-reading the thread, it seems part of the question is why the word
"Standard" is present in "(UTC-03:00) Brasilia Standard Time (Sao Paulo)".
That's due to the 184 day rule, specified in the "TypeFallback" section of
the TR35 spec that ICU uses to interpret CLDR data.  See
https://www.unicode.org/reports/tr35/tr35-dates.html#Using_Time_Zone_Names

.NET tries to side-step that, in the FixupTimeZoneGenericDisplayName
function here:
https://github.com/dotnet/runtime/blob/f20509b9ea563da18af963976b7db0e523e6837e/src/native/libs/System.Globalization.Native/pal_timeZoneInfo.c#L98

Unfortunately, the fixup function only works if there's at least one zone
in the same metazone whose generic name doesn't get the 184 day rule
applied.  So it can make all zones that have a generic name of "Mountain
Standard Time" use the name "Mountain Time" instead (thanks to
America/Phoenix), but it can't find any alternative to "Brasilia Standard
Time" because all the zones in "Brasilia" metazone all have the same
generic name, "Brasilia Standard Time".   (Note, one can't simply remove
words from the string, because you'd have to account for the equivalent of
"Standard" in every supported language).

Metazones are in CLDR here:
https://github.com/unicode-org/cldr/blob/main/common/supplemental/metaZones.xml

And here's the language entry for English for the Brasilia metazone:
https://github.com/unicode-org/cldr/blob/657e6e996b88b891132db29cc4fc61c36f174689/common/main/en.xml#L4168-L4174

<metazone type="Brasilia">
  <long>
    <generic>Brasilia Time</generic>
    <standard>Brasilia Standard Time</standard>
    <daylight>Brasilia Summer Time</daylight>
  </long>
</metazone>

So, you get "Brasilia Standard Time" because according to ICU, that is the
generic name for the Brasilia metazone, even though CLDR has "Brasilia
Time" - the 184 day rule applies and ICU overrides it with the standard
name.

Unfortunately, I have not found a way to get the generic name directly from
ICU4C (via a C API, not C++), that doesn't apply the 184 day rule.

-Matt


On Thu, Oct 12, 2023 at 9:53 AM Matt Johnson-Pint <mattjohnsonpint at gmail.com>
wrote:

> Hi.  I contributed most of the improvements to time zone display name
> logic for .NET, so I can offer some clarity here.  The original PR for this
> work is at: https://github.com/dotnet/runtime/pull/48931.  There's also a
> detailed blog post here:
>
> https://devblogs.microsoft.com/dotnet/date-time-and-time-zone-enhancements-in-net-6/#time-zone-display-names-on-linux-and-macos
>
> First off - Ignore anything related to the System.TimeZone class.  That
> only exists for legacy reasons and is generally considered deprecated.
> Only System.TimeZoneInfo should be used in .NET today.
>
> As of .NET 6, when running on Linux or MacOS, TimeZoneInfo gets its
> display names from ICU4C.  As you suspected, it cannot leverage ICU4J.  It
> is also limited to the C APIs.  It cannot use ICU's C++ APIs.
>
> To construct time zone display names, .NET makes calls to both
> ucal_getTimeZoneDisplayName, and udat_format with several
> different formats. You can see these ICU calls and others in the following
> file:
>
>
> https://github.com/dotnet/runtime/blob/f20509b9ea563da18af963976b7db0e523e6837e/src/native/libs/System.Globalization.Native/pal_timeZoneInfo.c
>
> You'll also want to look at the implementation of the
> GetFullValueForDisplayNameField method here:
>
>
> https://github.com/dotnet/runtime/blob/f20509b9ea563da18af963976b7db0e523e6837e/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.Unix.cs#L156
>
> (git hashes are just the current values, for permalink purposes)
>
> Between the two files, you'll notice that to use ICU alone to build a list
> of time zone display names is quite challenging.  It's not quite as simple
> as just taking the LONG_GENERIC of each one.  For example, if you do that,
> you'll find at least 13 different entries that all have the same display
> name, "Mountain Standard Time".  While useful on a single zone, they're
> useless when distinguishing one zone from the next in a list.
> Additionally, the word "Standard" can get in the way.  Ultimately one
> desires names like "Mountain Time (Phoenix)" vs "Mountain Time (Denver)" -
> so that they are disambiguated.
>
> There are lots of other edge cases though, and some of it requires an
> opinionated mindset.  For example, see some of the overrides here:
>
>
> https://github.com/dotnet/runtime/blob/f20509b9ea563da18af963976b7db0e523e6837e/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.Unix.cs#L17-L25
>
> Hope that helps.  Feel free to ask any follow up questions.
>
> -Matt
>
>
> On Tue, Oct 10, 2023 at 5:12 PM Guy Harris via tz <tz at iana.org> wrote:
>
>> On Oct 10, 2023, at 4:48 PM, Doug Ewell <doug at ewellic.org> wrote:
>>
>> > This is what we get from a known, reliable source that offers
>> human-readable names in English (or, importantly, 140 other languages)
>> corresponding to tzids. Some of the names are probably not perfect for a
>> picker. We already know that “(other)” and “(most locations)” are not
>> always perfect either, especially when presented out of context: Other than
>> what? What does “most” include?
>>
>> Perhaps it's time for somebody to run some UI experiments to determine
>> what works best for what groups of users.  (Complaints that you live in
>> city X but the selector only offers city Y count against "works best".)
>>
>> > We’re using a standard .NET Core call that queries ICU in a Linux
>> container, and makes a Windows call when running on Windows. There are no
>> options to select names for standard, daylight, or generic, and I don’t
>> know why it chooses standard by default. The ICU folks might know.
>>
>> I'm guessing it's easier for .NET languages to use ICU4C than ICU4J; the
>> icu::TimeZone Class Reference:
>>
>>
>> https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1TimeZone.html#a07cc5464421c1ae84f55ada930cf03df
>>
>> appears to offer several flavors of the getDisplayName method, with:
>>
>>         UnicodeString &getDisplayName(UBool inDaylight, EDisplayType
>> style, UnicodeString &result) const
>>
>> and
>>
>>         UnicodeString &getDisplayName(UBool inDaylight, EDisplayType
>> style, const Locale &locale, UnicodeString &result) const
>>
>> having the style argument, which is an EDisplayType:
>>
>>
>> https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1TimeZone.html#a07cc5464421c1ae84f55ada930cf03df
>>
>> I'm guessing that SHORT and LONG use the inDaylight value to determine
>> whether to provide the short or long standard time or the short or long
>> daylight time name, and SHORT_GENERIC and LONG_GENERIC provide the short or
>> long generic name.
>>
>> At least from what I see on Microsoft Learn, the .NET 7.0 TimeZone class:
>>
>>
>> https://learn.microsoft.com/en-us/dotnet/api/system.timezone?view=net-7.0
>>
>> only has the standard and daylight names as properties
>>
>> However, the .NET 7.0 TimeZoneInfo class:
>>
>>
>> https://learn.microsoft.com/en-us/dotnet/api/system.timezoneinfo?view=net-7.0
>>
>> also has the DisplayName property, which "Gets the general display name
>> that represents the time zone", and might provide the generic name when
>> it's using ICU.
>>
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mm.icann.org/pipermail/tz/attachments/20231012/769a2459/attachment.htm>


More information about the tz mailing list