Jak tłumaczyć między strefami czasowymi Windows i IANA?

149

Jak opisano na wiki tagów stref czasowych , istnieją dwa różne style stref czasowych.

  • Te dostarczane przez firmę Microsoft do użytku z systemem Windows i TimeZoneInfoklasą .Net (w przypadku uruchamiania w systemie Windows) są identyfikowane za pomocą wartości, takiej jak "Eastern Standard Time".

  • Te dostarczane przez IANA w TZDB i używane przez TimeZoneInfoklasę .NET podczas pracy w systemie Linux lub OSX są identyfikowane za pomocą wartości, takiej jak "America/New_York".

Wiele interfejsów API opartych na Internecie korzysta ze stref czasowych IANA, ale z wielu powodów może być konieczne przekonwertowanie ich na identyfikator strefy czasowej systemu Windows lub odwrotnie.

Jak można to osiągnąć w .Net?

Matt Johnson-Pint
źródło

Odpowiedzi:

198

Głównym źródłem danych do konwersji między identyfikatorami stref czasowych systemu Windows i IANA jest windowsZones.xmlplik rozpowszechniany w ramach projektu Unicode CLDR . Najnowszą wersję deweloperską można znaleźć tutaj .

Jednak CLDR jest wydawany tylko dwa razy w roku. To, wraz z okresową częstotliwością aktualizacji systemu Windows i nieregularnymi aktualizacjami bazy danych stref czasowych IANA, utrudnia po prostu bezpośrednie użycie danych CLDR. Należy pamiętać, że same zmiany stref czasowych są dokonywane z kaprysu różnych rządów na świecie i nie wszystkie zmiany są wprowadzane z odpowiednim wyprzedzeniem, aby mogły zostać wprowadzone do tych cykli wydawniczych przed ich datą wejścia w życie.

Istnieje kilka innych skrajnych przypadków, które należy rozpatrzyć, a które nie są objęte ścisłym rozporządzeniem CLDR, i od czasu do czasu pojawiają się nowe. Dlatego zawarłem złożoność rozwiązania w mikro-bibliotece TimeZoneConverter , którą można zainstalować z Nuget.

Korzystanie z tej biblioteki jest proste. Oto kilka przykładów konwersji:

string tz = TZConvert.IanaToWindows("America/New_York");
// Result:  "Eastern Standard Time"

string tz = TZConvert.WindowsToIana("Eastern Standard Time");
// result:  "America/New_York"

string tz = TZConvert.WindowsToIana("Eastern Standard Time", "CA");
// result:  "America/Toronto"

Więcej przykładów znajduje się na stronie projektu .

Należy pamiętać, że podczas gdy strefę czasową IANA można odwzorować na jedną strefę czasową systemu Windows, sytuacja odwrotna nie jest prawdą. Pojedyncza strefa czasowa Windows może być mapowana na więcej niż jedną strefę czasową IANA. Można to zobaczyć w powyższych przykładach, gdzie Eastern Standard Timejest odwzorowany zarówno na America/New_York, jak i na America/Toronto. TimeZoneConverter dostarczy ten oznaczony przez CLDR "001", zwany „złotą strefą”, chyba że podasz konkretnie kod kraju i pasuje do innej strefy w tym kraju.

Uwaga: ta odpowiedź ewoluowała przez lata, więc poniższe komentarze mogą, ale nie muszą, odnosić się do bieżącej wersji. Sprawdź historię zmian, aby uzyskać szczegółowe informacje. Dzięki.

Matt Johnson-Pint
źródło
1
używając tej metody podczas konwersji (GMT+05:30) Chennai, Kolkata, Mumbai, New Delhidaje Asia/Calcuttato, co powinno Asia/Kolkata. wygląda na to, że TzdbDateTimeZoneSourcezawiera stare wartości.
Anto Subash
1
@MattJohnson podczas konwersji metody Asia/Kolkatausing IanaToWindowskończy się niepowodzeniem. ale działa z Asia/Calcuttaktórą jest stara nazwa. zaktualizowałeś metodę, WindowsToIanaale masz IanaToWindowsten sam problem. Kilka innych stref, które nie pracują są America/Argentina/Buenos_Aires, America/Indiana/Indianapolis, Asia/Kathmandu.
Anto Subash
1
@AntoJSubash - Ponownie wspaniała obserwacja! Dokonałem edycji IanaToWindowsmetody kompensacji. Dziękuję bardzo!
Matt Johnson-Pint
2
@MattJohnson I second @ sirrocco's obserwacja. Używanie identyfikatora kanonicznego też var canonical = tzdbSource.CanonicalIdMap[ ianaZoneId ]; links = Enumerable.Repeat( canonical, 1 ).Concat( links );pomogło mi.
Johannes Rudolph
2
@sirrocco - Przepraszamy, nie widziałem wcześniej Twojego komentarza. Zaktualizowano funkcje. Dzięki!
Matt Johnson-Pint
4

Wiem, że to stare pytanie, ale miałem przypadek użycia, który chciałbym tutaj udostępnić, ponieważ jest to najbardziej odpowiedni post, który znalazłem podczas wyszukiwania. Opracowywałem aplikację .NET Core przy użyciu kontenera docker linux, ale do wdrożenia na serwerze Windows. Potrzebowałem więc tylko kontenera docker linux do obsługi nazw stref czasowych systemu Windows. Mam to działające bez zmiany kodu aplikacji, wykonując następujące czynności:

cp /usr/share/zoneinfo/America/Chicago "/usr/share/zoneinfo/Central Standard Time"
cp /usr/share/zoneinfo/America/New_York "/usr/share/zoneinfo/Eastern Standard Time"
cp /usr/share/zoneinfo/America/Denver "/usr/share/zoneinfo/Mountain Standard Time"
cp /usr/share/zoneinfo/America/Los_Angeles "/usr/share/zoneinfo/Pacific Standard Time"

Następnie w moim kodzie .NET działały bez żadnych modyfikacji: TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")

EverPresent
źródło
1
To całkiem niezłe nieszablonowe myślenie! Wydaje się, że powinno być w porządku, o ile obejmujesz kilka określonych stref czasowych. Pamiętaj, że w USA jest więcej niż tych czterech. Aby objąć 50 stanów w dzisiejszych czasach, musisz również dodać linki America/Phoenixdo "US Mountain Standard Time", Pacific/Honoluludo "Hawaiian Standard Time", America/Anchoragedo "Alaskan Standard Time"i America/Adakdo "Aleutian Standard Time". Nie obejmuje to terytoriów USA ani historycznych rozbieżności, ale na początek.
Matt Johnson-Pint,
2
Nie polecałbym tego podejścia, jeśli zamierzasz objąć cały świat lub mieć do czynienia z jakimkolwiek ważnym identyfikatorem strefy czasowej. Lista jest na to zbyt długa i niestabilna.
Matt Johnson-Pint,