Bagaimana cara menerjemahkan antara zona waktu Windows dan IANA?

149

Seperti dijelaskan dalam wiki tag zona waktu , ada dua gaya zona waktu yang berbeda.

  • Yang disediakan oleh Microsoft untuk digunakan dengan Windows dan TimeZoneInfokelas .Net (saat berjalan di Windows) diidentifikasi oleh nilai seperti "Eastern Standard Time".

  • Yang disediakan oleh IANA di TZDB, dan digunakan oleh TimeZoneInfokelas .NET saat berjalan di Linux atau OSX, diidentifikasi oleh nilai seperti "America/New_York".

Banyak API berbasis Internet menggunakan zona waktu IANA, tetapi karena berbagai alasan orang mungkin perlu mengonversinya ke id zona waktu Windows, atau sebaliknya.

Bagaimana ini bisa dilakukan di .Net?

Matt Johnson-Pint
sumber

Jawaban:

198

Sumber utama data untuk konversi antara Windows dan pengidentifikasi zona waktu IANA adalah windowsZones.xmlfile, didistribusikan sebagai bagian dari proyek Unicode CLDR . Versi dev terbaru dapat ditemukan di sini .

Namun , CLDR dirilis hanya dua kali setahun. Ini, bersama dengan irama berkala pembaruan Windows, dan pembaruan tidak teratur dari basis data zona waktu IANA, membuatnya rumit untuk hanya menggunakan data CLDR secara langsung. Ingatlah bahwa perubahan zona waktu dilakukan atas keinginan berbagai pemerintah di dunia, dan tidak semua perubahan dilakukan dengan pemberitahuan yang cukup untuk menjadikannya dalam siklus rilis ini sebelum tanggal efektif masing-masing.

Ada beberapa kasus tepi lainnya yang perlu ditangani yang tidak dicakup secara ketat oleh CLDR, dan yang baru muncul dari waktu ke waktu. Oleh karena itu, saya telah merangkum kerumitan solusi ke dalam perpustakaan mikro TimeZoneConverter , yang dapat diinstal dari Nuget.

Menggunakan perpustakaan ini sederhana. Berikut adalah beberapa contoh konversi:

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"

Ada lebih banyak contoh di situs proyek .

Penting untuk diketahui bahwa sementara zona waktu IANA dapat dipetakan ke zona waktu Windows tunggal, kebalikannya tidak benar. Satu zona waktu Windows mungkin dipetakan ke lebih dari satu zona waktu IANA. Ini dapat dilihat pada contoh di atas, di mana Eastern Standard Timedipetakan untuk keduanya America/New_York, dan untuk America/Toronto. TimeZoneConverter akan memberikan yang ditandai oleh CLDR "001", yang dikenal sebagai "zona emas", kecuali jika Anda secara spesifik memberikan kode negara dan ada kecocokan untuk zona berbeda di negara itu.

Catatan: Jawaban ini telah berkembang selama bertahun-tahun, jadi komentar di bawah ini mungkin atau mungkin tidak berlaku untuk revisi saat ini. Tinjau riwayat edit untuk detailnya. Terima kasih.

Matt Johnson-Pint
sumber
1
menggunakan metode ini ketika mengkonversi (GMT+05:30) Chennai, Kolkata, Mumbai, New Delhimemberi Asia/Calcuttaitu seharusnya Asia/Kolkata. sepertinya TzdbDateTimeZoneSourceberisi nilai-nilai lama.
Anto Subash
1
@MattJohnson saat mengonversi metode Asia/Kolkatamenggunakan IanaToWindows, gagal. tetapi berfungsi dengan Asia/Calcuttanama lama. Anda telah memperbarui metode ini WindowsToIanatetapi IanaToWindowsjuga memiliki masalah yang sama. beberapa zona lain yang tidak bekerja adalah America/Argentina/Buenos_Aires, America/Indiana/Indianapolis, Asia/Kathmandu.
Anto Subash
1
@AntoJSubash - Sekali lagi, pengamatan yang bagus! Saya telah mengedit IanaToWindowsmetode untuk kompensasi. Terima kasih banyak!
Matt Johnson-Pint
2
@MattJohnson I kedua @ pengamatan Sirrocco. Menggunakan id kanonik juga seperti var canonical = tzdbSource.CanonicalIdMap[ ianaZoneId ]; links = Enumerable.Repeat( canonical, 1 ).Concat( links );melakukan trik untuk saya.
Johannes Rudolph
2
@sirrocco - Maaf saya tidak melihat komentar Anda lebih cepat. Diperbarui fungsi. Terima kasih!
Matt Johnson-Pint
4

Saya tahu ini adalah pertanyaan lama, tetapi saya memiliki use case yang saya pikir akan saya bagikan di sini, karena ini adalah posting paling relevan yang saya temukan ketika mencari. Saya sedang mengembangkan aplikasi .NET Core menggunakan wadah docker linux, tetapi untuk ditempatkan di server windows. Jadi saya hanya perlu wadah docker linux saya untuk mendukung nama zona waktu windows. Saya mendapatkan ini berfungsi tanpa mengubah kode aplikasi saya dengan melakukan hal berikut:

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"

Kemudian, dalam kode .NET saya, yang berikut ini berfungsi tanpa modifikasi apa pun: TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")

Hadir
sumber
1
Itu pemikiran yang luar biasa! Ini sepertinya baik-baik saja, asalkan Anda mencakup beberapa zona waktu tertentu. Perlu diingat bahwa ada lebih dari empat di AS. Untuk mencakup 50 negara bagian saat ini, Anda juga perlu menambahkan tautan America/Phoenixke "US Mountain Standard Time", Pacific/Honoluluke "Hawaiian Standard Time", America/Anchorageke "Alaskan Standard Time", dan America/Adakke "Aleutian Standard Time". Itu tidak mencakup wilayah AS atau perbedaan sejarah, tetapi akan membantu Anda memulai.
Matt Johnson-Pint
2
Saya tidak akan merekomendasikan pendekatan ini jika niat seseorang adalah untuk mencakup seluruh dunia atau untuk berurusan dengan pengidentifikasi zona waktu yang valid. Daftarnya terlalu panjang dan terlalu tidak stabil untuk itu.
Matt Johnson-Pint