如何在 Windows 和 IANA 时区之间进行翻译

本文关键字:之间 翻译 时区 IANA Windows | 更新日期: 2023-09-27 18:20:06

如时区标签维基中所述,有两种不同风格的时区。

  • Microsoft提供的用于 Windows 和 .Net TimeZoneInfo类(在 Windows 上运行时(的那些由 "Eastern Standard Time" 等值标识。

  • 那些由 IANA 在 TZDB 中提供,并在 Linux 或 OSX 上运行时由 .NET TimeZoneInfo 类使用的那些由 "America/New_York" 等值标识。

许多基于 Internet 的 API 使用 IANA 时区,但由于多种原因,可能需要将其转换为 Windows 时区 ID,反之亦然。

如何在 .Net 中完成此操作?

如何在 Windows 和 IANA 时区之间进行翻译

当前状态:

从 .NET 6 开始,任何同时安装了时区数据和 ICU 的平台都支持这两种形式的时区,这是大多数 Windows、Linux 和 MacOS 安装。 参见托比亚斯的回答。

原答案:

用于在 Windows 和 IANA 时区标识符之间进行转换的主要数据源是作为 Unicode CLDR 项目的一部分分发的windowsZones.xml文件。 可以在此处找到最新的开发版本。

但是,CLDR 每年只发布两次。 这一点,再加上Windows更新的周期性节奏,以及IANA时区数据库的不定期更新,使得直接使用CLDR数据变得复杂。 请记住,时区更改本身是世界各国政府的心血来潮,并非所有更改都经过足够的通知,以便在各自的生效日期之前进入这些发布周期。

还有一些其他边缘情况需要处理,CLDR 没有严格涵盖,并且不时弹出新的边缘情况。 因此,我已将解决方案的复杂性封装到可以从 Nuget 安装的 TimeZoneConverter 微型库中。

使用此库很简单。 以下是一些转化示例:

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_YorkAmerica/Toronto。 TimeZoneConverter 将提供 CLDR 用 "001" 标记的那个,称为"黄金区域",除非您特别提供国家/地区代码并且该国家/地区的不同区域匹配。

注意:这个答案多年来一直在变化,因此下面的评论可能适用于也可能不适用于当前的修订版。 查看编辑历史记录以了解详细信息。 谢谢。

从 .NET 6 开始,终于可以跨平台方式使用时区,因此不再需要这些手动解决方法。

TimeZoneInfo.FindSystemTimeZoneById(string)方法会自动接受任一平台上的 Windows 或 IANA 时区,并在需要时进行转换。

// Both of these will now work on any supported OS where ICU and time zone data are available.
TimeZoneInfo tzi1 = TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time");
TimeZoneInfo tzi2 = TimeZoneInfo.FindSystemTimeZoneById("Australia/Sydney");

请注意,如链接中所述,默认情况下,基于 .NET Core Alpine Linux 的 Docker 映像没有安装必要的tzdata,因此必须在Dockerfile中安装它才能正常工作。

我知道这是一个老问题,但我有一个用例,我想在这里分享,因为这是我在搜索时发现的最相关的帖子。我正在使用docker linux容器开发一个.NET Core应用程序,但用于在Windows服务器上部署。所以我只需要我的 docker 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")