วิธีแปลระหว่างเขตเวลา Windows และ IANA


149

ตามที่อธิบายไว้ในวิกิแท็กเขตเวลามีสองสไตล์ที่แตกต่างกันของเขตเวลา

  • ผู้ให้บริการโดยไมโครซอฟท์สำหรับการใช้งานกับ Windows และสุทธิTimeZoneInfoระดับ (เมื่อทำงานบน Windows) "Eastern Standard Time"จะระบุค่าเช่น

  • ผู้ให้บริการโดย IANA ใน TZDB และใช้โดยสุทธิTimeZoneInfoระดับเมื่อทำงานบนลินุกซ์หรือ OSX "America/New_York"จะระบุค่าเช่น

API บนอินเทอร์เน็ตจำนวนมากใช้เขตเวลาของ IANA แต่ด้วยเหตุผลหลายประการเราอาจจำเป็นต้องแปลงเป็นรหัสโซนเวลาของ Windows หรือในทางกลับกัน

วิธีนี้สามารถทำได้ใน. Net

คำตอบ:


198

แหล่งที่มาหลักของข้อมูลสำหรับการแปลงระหว่าง Windows และตัวระบุเขตเวลาของ IANA คือwindowsZones.xmlไฟล์ซึ่งถูกแจกจ่ายเป็นส่วนหนึ่งของโครงการUnicode CLDR รุ่น dev ล่าสุดสามารถพบได้ที่นี่

อย่างไรก็ตาม CLDR ถูกปล่อยออกปีละสองครั้งเท่านั้น สิ่งนี้พร้อมกับจังหวะการปรับปรุงของ Windows เป็นระยะและการปรับปรุงฐานข้อมูลเขตเวลาของ IANA อย่างไม่สม่ำเสมอทำให้มีความซับซ้อนในการใช้ข้อมูล CLDR โดยตรง โปรดทราบว่าการเปลี่ยนแปลงเขตเวลาเกิดขึ้นในรัฐบาลต่าง ๆ ของโลกและไม่มีการเปลี่ยนแปลงใด ๆ ที่เกิดขึ้นเมื่อมีการแจ้งเตือนอย่างเพียงพอที่จะทำให้มันเข้าสู่วงจรการเปิดตัวเหล่านี้ก่อนวันที่มีผลบังคับใช้

มีอีกสองสามกรณีที่จำเป็นต้องได้รับการจัดการที่ไม่ได้รับการคุ้มครองอย่างเข้มงวดจาก CLDR และมีกรณีใหม่ ๆ เกิดขึ้นเป็นครั้งคราว ดังนั้นฉันได้ห่อหุ้มความซับซ้อนของการแก้ปัญหาไว้ในห้องสมุดขนาดเล็กTimeZoneConverterซึ่งสามารถติดตั้งได้จาก Nuget

การใช้ห้องสมุดนี้เป็นเรื่องง่าย นี่คือตัวอย่างของการแปลง:

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"

มีตัวอย่างอื่น ๆบนเว็บไซต์ของโครงการ

สิ่งสำคัญคือต้องทราบว่าในขณะที่เขตเวลาของ IANA สามารถแมปกับเขตเวลา Windows เดียวการย้อนกลับไม่เป็นความจริง เขตเวลา Windows เดียวอาจถูกแมปกับเขตเวลาของ IANA มากกว่าหนึ่งเขต นี้สามารถเห็นได้ในตัวอย่างข้างต้นที่Eastern Standard Timeถูกแมปไปทั้งสองและAmerica/New_York America/TorontoTimeZoneConverter จะส่งมอบที่ CLDR ทำเครื่องหมายด้วย"001"หรือที่เรียกว่า "โซนทองคำ" ยกเว้นว่าคุณระบุรหัสประเทศโดยเฉพาะและมีการจับคู่สำหรับโซนที่แตกต่างกันในประเทศนั้น ๆ

หมายเหตุ: คำตอบนี้มีวิวัฒนาการมาหลายปีดังนั้นความคิดเห็นด้านล่างอาจมีหรือไม่มีผลกับการแก้ไขปัจจุบัน ตรวจสอบประวัติการแก้ไขเพื่อดูรายละเอียด ขอบคุณ


1
ใช้วิธีนี้เมื่อมีการแปลง(GMT+05:30) Chennai, Kolkata, Mumbai, New Delhiให้มันควรจะเป็นAsia/Calcutta Asia/Kolkataดูเหมือนว่าTzdbDateTimeZoneSourceมีค่าเก่า
Anto Subash

1
@MattJohnson ในขณะที่แปลง วิธีการAsia/Kolkataใช้IanaToWindowsมันล้มเหลว แต่มันใช้งานได้Asia/Calcuttaซึ่งเป็นชื่อเก่าคุณได้ทำการปรับปรุงวิธีการแล้วWindowsToIanaแต่IanaToWindowsก็มีปัญหาเดียวกัน ไม่กี่โซนอื่น ๆ ที่ไม่ได้ทำงานอยู่America/Argentina/Buenos_Aires, ,America/Indiana/Indianapolis Asia/Kathmandu
Anto Subash

1
@AntoJSubash - อีกครั้งการสังเกตที่ยอดเยี่ยม! ฉันได้แก้ไขIanaToWindowsวิธีการชดเชยแล้ว ขอบคุณมาก ๆ!
Matt Johnson-Pint

2
@ MattJohnson ฉันสอง @ การสังเกตของ sirrocco การใช้รหัส canonical เหมือนกันvar canonical = tzdbSource.CanonicalIdMap[ ianaZoneId ]; links = Enumerable.Repeat( canonical, 1 ).Concat( links );ก็เป็นเคล็ดลับสำหรับฉัน
โยฮันเนสรูดอล์ฟ

2
@sirrocco - ขออภัยฉันไม่เห็นความคิดเห็นของคุณเร็วกว่านี้ อัพเดทฟังก์ชั่น ขอบคุณ!
Matt Johnson-Pint

4

ฉันรู้ว่านี่เป็นคำถามเก่า แต่ฉันมีกรณีการใช้งานแม้ว่าฉันจะแบ่งปันที่นี่เนื่องจากนี่เป็นโพสต์ที่เกี่ยวข้องที่สุดที่ฉันพบเมื่อค้นหา ฉันกำลังพัฒนาแอพ. NET Core โดยใช้คอนเทนเนอร์ linux docker แต่สำหรับการปรับใช้บนเซิร์ฟเวอร์ windows ดังนั้นฉันต้องการเพียงนักเทียบท่าคอนเทนเนอร์ linux เพื่อสนับสนุนชื่อเขตเวลา windows ฉันใช้งานได้โดยไม่ต้องเปลี่ยนรหัสแอปพลิเคชันของฉันโดยทำสิ่งต่อไปนี้:

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"

จากนั้นในรหัส. NET ของฉันการทำงานต่อไปนี้โดยไม่มีการดัดแปลงใด ๆ : TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")


1
นั่นคือความคิดนอกกรอบที่ยอดเยี่ยม! ดูเหมือนว่ามันจะโอเคตราบใดที่คุณครอบคลุมบางโซนเวลา โปรดจำไว้ว่ามีมากกว่าสี่ในสหรัฐอเมริกา เพื่อให้ครอบคลุม 50 รัฐในปัจจุบันนอกจากนี้คุณยังจะต้องเพิ่มการเชื่อมโยงAmerica/Phoenixไป"US Mountain Standard Time", Pacific/Honoluluไป"Hawaiian Standard Time", America/Anchorageไป"Alaskan Standard Time"และจะAmerica/Adak "Aleutian Standard Time"ไม่ครอบคลุมถึงดินแดนของสหรัฐฯหรือความขัดแย้งในอดีต แต่จะให้คุณเริ่มต้น
Matt Johnson-Pint

2
ฉันจะไม่แนะนำวิธีการนี้หากเจตนาของเราคือครอบคลุมทั้งโลกหรือเพื่อจัดการกับตัวระบุเขตเวลาที่ถูกต้อง รายการยาวเกินไปและผันผวนเกินไป
Matt Johnson-Pint
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.