Làm cách nào để dịch giữa các múi giờ của Windows và IANA?


149

Như được mô tả trong wiki thẻ múi giờ , có hai kiểu múi giờ khác nhau.

  • Những thứ được Microsoft cung cấp để sử dụng với Windows và TimeZoneInfolớp .Net (khi chạy trên Windows) được xác định bởi một giá trị như "Eastern Standard Time".

  • Những thứ được IANA cung cấp trong TZDB và được sử dụng bởi TimeZoneInfolớp .NET khi chạy trên Linux hoặc OSX, được xác định bởi một giá trị như "America/New_York".

Nhiều API dựa trên Internet sử dụng múi giờ IANA, nhưng vì nhiều lý do, người ta có thể cần phải chuyển đổi nó thành id múi giờ của Windows hoặc ngược lại.

Làm thế nào điều này có thể được thực hiện trong .Net?

Câu trả lời:


198

Nguồn chính của dữ liệu để chuyển đổi giữa các định danh múi giờ của Windows và IANA là windowsZones.xmltệp, được phân phối như một phần của dự án Unicode CLDR . Phiên bản dev mới nhất có thể được tìm thấy ở đây .

Tuy nhiên , CLDR chỉ được phát hành hai lần mỗi năm. Điều này, cùng với nhịp điệu định kỳ của các bản cập nhật Windows và các bản cập nhật bất thường của cơ sở dữ liệu múi giờ IANA, khiến việc sử dụng trực tiếp dữ liệu CLDR trở nên phức tạp. Hãy nhớ rằng chính các thay đổi múi giờ được thực hiện theo ý thích của các chính phủ khác nhau trên thế giới và không phải tất cả các thay đổi đều được thực hiện với thông báo đầy đủ để đưa nó vào các chu kỳ phát hành này trước ngày có hiệu lực tương ứng.

Có một vài trường hợp cạnh khác cần được xử lý mà không được CLDR bảo vệ nghiêm ngặt và những trường hợp mới thỉnh thoảng xuất hiện. Do đó, tôi đã gói gọn sự phức tạp của giải pháp vào thư viện vi mô TimeZoneConverter , có thể được cài đặt từ Nuget.

Sử dụng thư viện này là đơn giản. Dưới đây là một số ví dụ về chuyển đổi:

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"

Có nhiều ví dụ trên trang web của dự án .

Điều quan trọng là phải nhận ra rằng trong khi múi giờ IANA có thể được ánh xạ tới một múi giờ Windows duy nhất, thì điều ngược lại là không đúng. Một múi giờ Windows duy nhất có thể được ánh xạ tới nhiều múi giờ IANA. Điều này có thể được nhìn thấy trong các ví dụ trên, nơi Eastern Standard Timeđược ánh xạ tới cả hai America/New_YorkAmerica/Toronto. TimeZoneConverter sẽ phân phối cái mà CLDR đánh dấu "001", được gọi là "khu vực vàng", trừ khi bạn cung cấp cụ thể mã quốc gia và có một trận đấu cho một khu vực khác ở quốc gia đó.

Lưu ý: Câu trả lời này đã phát triển qua nhiều năm, vì vậy các bình luận bên dưới có thể hoặc không thể áp dụng cho bản sửa đổi hiện tại. Xem lại lịch sử chỉnh sửa để biết chi tiết. Cảm ơn.


1
sử dụng phương pháp này khi chuyển đổi (GMT+05:30) Chennai, Kolkata, Mumbai, New Delhisẽ cho Asia/CalcuttaAsia/Kolkata. có vẻ như TzdbDateTimeZoneSourcechứa các giá trị cũ.
Anto Subash 17/03/2016

1
@MattJohnson trong khi chuyển đổi phương thức Asia/Kolkatasử dụng IanaToWindows, nó thất bại. nhưng nó hoạt động với Asia/Calcuttatên cũ. Bạn đã cập nhật phương thức WindowsToIananhưng IanaToWindowscũng có vấn đề tương tự. vài khu khác không làm việc được America/Argentina/Buenos_Aires, America/Indiana/Indianapolis, Asia/Kathmandu.
Anto Subash

1
@AntoJSubash - Một lần nữa, quan sát tuyệt vời! Tôi đã chỉnh sửa IanaToWindowsphương pháp để bù. Cảm ơn rất nhiều!
Matt Johnson-Pint

2
@MattJohnson Tôi quan sát thứ hai @ sirrocco. Sử dụng id canonical quá giống như var canonical = tzdbSource.CanonicalIdMap[ ianaZoneId ]; links = Enumerable.Repeat( canonical, 1 ).Concat( links );đã lừa tôi.
Julian Rudolph

2
@sirrocco - Xin lỗi tôi đã không thấy bình luận của bạn sớm hơn. Cập nhật các chức năng. Cảm ơn!
Matt Johnson-Pint

4

Tôi biết đây là một câu hỏi cũ, nhưng tôi đã có một trường hợp sử dụng mặc dù tôi sẽ chia sẻ ở đây, vì đây là bài viết phù hợp nhất tôi tìm thấy khi tìm kiếm. Tôi đã phát triển ứng dụng .NET Core bằng cách sử dụng bộ chứa linux docker, nhưng để triển khai trên máy chủ windows. Vì vậy, tôi chỉ cần container linux docker của tôi để hỗ trợ các tên múi giờ của windows. Tôi đã làm việc này mà không thay đổi mã ứng dụng của mình bằng cách làm như sau:

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"

Sau đó, trong mã .NET của tôi, phần sau hoạt động mà không có bất kỳ sửa đổi nào: TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")


1
Đó là một số suy nghĩ tuyệt vời! Điều này có vẻ như là ổn, miễn là bạn đang bao gồm một vài múi giờ cụ thể. Hãy nhớ rằng có nhiều hơn bốn người ở Mỹ. Để trang trải 50 tiểu bang trong ngày nay, bạn cũng sẽ cần phải thêm các liên kết cho America/Phoenixđến "US Mountain Standard Time", Pacific/Honoluluđến "Hawaiian Standard Time", America/Anchorageđến "Alaskan Standard Time", và America/Adakđể "Aleutian Standard Time". Điều đó không bao gồm các lãnh thổ hoặc sự khác biệt lịch sử của Hoa Kỳ, nhưng sẽ giúp bạn bắt đầu.
Matt Johnson-Pint

2
Tôi sẽ không đề xuất phương pháp này nếu mục đích của một người là bao quát toàn thế giới hoặc để đối phó với bất kỳ định danh múi giờ hợp lệ nào. Danh sách quá dài và quá biến động cho điều đó.
Matt Johnson-Pint
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.