tc39 / proposal-canonical-tz

TC39 Proposal (stacked on Temporal) to improve handling of changes to the IANA Time Zone Database
Other
38 stars 2 forks source link

Canonical IANA time zone name for Windows time zones #16

Open anba opened 1 year ago

anba commented 1 year ago

Not all OS use the IANA time zone database, most notably Windows. So it's not all always possible to directly return the OS time zone as an IANA time zone, because the OS time zone may first need to be mapped to a corresponding IANA time zone.

For example the Windows time zone "India Standard Time" can be either mapped to Asia/Kolkata or Asia/Calcutta. Or "FLE Standard Time" to either Europe:Kyiv or Europe/Kiev (assuming the user region is 001 (World) or UA (Ukraine)).

CLDR uses this mapping file: https://github.com/unicode-org/cldr/blob/main/common/supplemental/windowsZones.xml.

justingrant commented 1 year ago

Agree, good issue. Windows zones need a mapping between native zones and IANA.

My understanding is that Windows now offers a built-in IANA zone mapping in .NET 6.0 which was released in November 2021. https://devblogs.microsoft.com/dotnet/date-time-and-time-zone-enhancements-in-net-6/#time-zone-conversion-apis

// Conversion from Windows to IANA when a region is unknown
string winId2 = "Eastern Standard Time";
if (!TimeZoneInfo.TryConvertWindowsIdToIanaId(winId2, out string ianaId2))
    throw new TimeZoneNotFoundException($"No IANA time zone found for "{winId2}".");
Console.WriteLine($"{winId2} => {ianaId2}");  // "Eastern Standard Time => America/New_York"

// Conversion from Windows to IANA when a region is known
string winId3 = "Eastern Standard Time";
string region = "CA"; // Canada
if (!TimeZoneInfo.TryConvertWindowsIdToIanaId(winId3, region, out string ianaId3))
    throw new TimeZoneNotFoundException($"No IANA time zone found for "{winId3}" in "{region}".");
Console.WriteLine($"{winId3} + {region} => {ianaId3}");  // "Eastern Standard Time + CA => America/Toronto"

Even though there's now an alternative to ICU's mapper, I don't know enough to have an opinion about whether it makes more sense to use ICU's mapper or to use .NET's mapper. I also don't know if Windows's implementation simply uses ICU under the covers. I do know that on non-Windows platforms, .NET 6+ does use ICU.

The .NET source code isn't clear on this, because it simply calls out to a native library that does the mapping.

I also wouldn't be surprised if, under the covers, .NET's mapper on Windows just uses ICU too. :-)

Perhaps someone from Microsoft who has access to Windows internal sources could clarify this. @mattjohnsonpint do you know?

mattjohnsonpint commented 1 year ago

Yes, that's exactly correct. .NET is using ICU for the mapping. If ICU is not available then the mapping doesn't work. ICU has been available on Windows since Windows 10 version 1703 ("Redstone 2", aka "Creators Update").

However, separate from .NET, Windows has it's own copy of the mappings at "C:\Windows\Globalization\Time Zone\timezoneMapping.xml". It's in a slightly different format, but it is ultimately derived from the same CLDR source. That one is used for native Windows APIs, such as Windows.Globalization.Calendar.GetTimeZone. It's been available since Windows 10 version 1507 ("Threshold").

mattjohnsonpint commented 1 year ago

In other words, in a C#/WinRT app (aka UWP), I can do:

string ianaTzId = new Windows.Globalization.Calendar().GetTimeZone();

Or from a PowerShell command line:

[Windows.Globalization.Calendar,Windows.Globalization,ContentType=WindowsRuntime]::New().GetTimeZone()

Similar functions are available from C++, as are ICU C functions such as ucal_getTimeZoneID().

mattjohnsonpint commented 1 year ago

Also - just FYI - I don't work at Microsoft anymore, and all of that is public info.

justingrant commented 1 year ago

Got it. Looks like the entire ecosystem on Windows (browsers, Node, .NET, and OS itself) depends on CLDR (via ICU) to do the Windows=>IANA time zone mapping. But the mappings used in Windows (at least the OS's copy) don't seem to follow ICU's "never change the canonical ID" model. See below for more info about this.

@mattjohnsonpint - Sorry I forgot you weren't at MSFT anymore! Do you know who'd be a good person who's still at the mothership to talk with about how Windows approaches the problem of when to override ICU's canonicalization and when to add new identifiers like Europe/Kyiv?

I just signed up for a Windows "Cloud VM" and looked at the timezone data files in the C:\Windows\Globalization\Time Zone folder. What I see is that in Windows, Asia/Kolkata and Asia/Ho_Chi_Minh are both canonical, which is different from ICU where Asia/Calcutta and Asia/Saigon (the old names) are still canonical. Furthermore, Europe/Kyiv isn't present at all, so Europe/Kiev is still canonical in Windows.

From C:\Windows\Globalization\Time Zone\timezoneMapping.xml:

<?xml version="1.0" encoding="utf-8"?>
<TimeZoneMapping GeneratedAt="2023-03-06T16:39:25.9181749+05:30">
. . .
  <MapTZ TZID="Asia/Kolkata" WinID="India Standard Time" Region="001" Default="true" StdPath="India/standard" DltPath="Asia/Calcutta/India/daylight" />
  <MapTZ TZID="Asia/Kolkata" WinID="India Standard Time" Region="IN" Default="true" StdPath="India/standard" DltPath="Asia/Calcutta/India/daylight" />
  <MapTZ TZID="Asia/Calcutta" WinID="India Standard Time" Region="IN" StdPath="India/standard" DltPath="Asia/Calcutta/India/daylight" />
. . .
  <MapTZ TZID="Europe/Kiev" WinID="FLE Standard Time" Region="001" Default="true" StdPath="Europe_Eastern/standard" DltPath="Europe_Eastern/daylight" />
. . .
  <MapTZ TZID="Europe/Kiev" WinID="FLE Standard Time" Region="UA" Default="true" StdPath="Europe_Eastern/standard" DltPath="Europe_Eastern/daylight" />

From C:\Windows\Globalization\Time Zone\timezones.xml:

?xml version="1.0" encoding="utf-8"?>
<TimeZoneInfo GeneratedAt="2023-03-06T16:39:25.8473397+05:30">
  <TimeZones>
. . . 
    <Zone ID="Asia/Kolkata">
      <TZI UtcOff="05:53:28" Format="India" Rules="" Until="1854-06-28T00:00:00" />
      <TZI UtcOff="05:53:20" Format="India" Rules="" Until="1870-01-01T00:00:00" />
      <TZI UtcOff="05:21:10" Format="India" Rules="" Until="1906-01-01T00:00:00" />
      <TZI UtcOff="05:30:00" Format="India" Rules="" Until="1941-10-01T00:00:00" />
      <TZI UtcOff="05:30:00" Format="India" LstOff="01:00:00" Until="1942-05-15T00:00:00" />
      <TZI UtcOff="05:30:00" Format="India" Rules="" Until="1942-09-01T00:00:00" />
      <TZI UtcOff="05:30:00" Format="India" LstOff="01:00:00" Until="1945-10-15T00:00:00" />
      <TZI UtcOff="05:30:00" Format="India" Rules="" />
    </Zone>
. . .
    <Zone ID="Europe/Kiev">
      <TZI UtcOff="02:02:04" Format="Europe_Eastern" Rules="" Until="1880-01-01T00:00:00" />
      <TZI UtcOff="02:02:04" Format="Europe_Eastern" Rules="" Until="1924-05-02T00:00:00" />
      <TZI UtcOff="02:00:00" Format="Europe_Eastern" Rules="" Until="1930-06-21T00:00:00" />
      <TZI UtcOff="03:00:00" Format="Europe_Eastern" Rules="" Until="1941-09-20T00:00:00" />
      <TZI UtcOff="01:00:00" Format="Europe_Eastern" Rules="C-Eur" Until="1943-11-06T00:00:00" />
      <TZI UtcOff="03:00:00" Format="Europe_Eastern" Rules="Russia" Until="1990-07-01T02:00:00" />
      <TZI UtcOff="02:00:00" Format="Europe_Eastern" LstOff="01:00:00" Until="1991-09-29T03:00:00" />
      <TZI UtcOff="02:00:00" Format="Europe_Eastern" Rules="E-Eur" Until="1995-01-01T00:00:00" />
      <TZI UtcOff="02:00:00" Format="Europe_Eastern" Rules="EU" />
    </Zone>
. . .
mattjohnsonpint commented 1 year ago

Unfortunately, I don't have any relevant contacts responsible for time zones at Microsoft anymore.

The only signal I have is from the MS DST Blog, and I don't have a direct connection to the primary contributor.

justingrant commented 1 year ago

@rbuckton - do you have a way to connect me with the author of the MS DST Blog that Matt links above? As part of this proposal, I have some questions about the process for Windows to update its time zone information. So far I've gotten input from many other platforms but still trying to find a good contact on the Windows side. Thanks!