如何在Windows和IANA时区之间进行转换?

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

  • 由Microsoft提供的用于Windows和.NETTimeZoneInfo类(当在Windows上运行时)的那些类由诸如"Eastern Standard Time"的值来标识。

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

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

如何在.NET中实现这一点?

83623 次浏览

当前状态:

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

原答案:

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

然而,CLDR每年仅发布两次。这一点,再加上Windows更新的周期性节奏,以及IANA时区数据库的不定期更新,使得直接使用CLDR数据变得非常复杂。请记住,时区更改本身是根据世界各国政府的意愿进行的,并不是所有的更改都在其各自的生效日期之前进行了足够的通知,以使其进入这些发布周期。

还有其他一些需要处理的边缘情况,这些情况并没有被CLDR严格覆盖,而且新的情况会不时出现。因此,我将解决方案的复杂性封装到时区转换器微库中,它可以从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_YorkAmerica/Toronto。TimeZoneConverter将提供CLDR用"001"标记的区域,称为“黄金区域”,除非您特别提供了国家代码,并且该国家的其他区域与之匹配。

注意:这个答案经过多年的演变,因此下面的评论可能适用于也可能不适用于当前的修订版。有关详细信息,请查看编辑历史记录。谢谢。

我知道这是一个老问题,但我有一个用例,我想我会在这里分享,因为这是我在搜索时找到的最相关的帖子。我正在使用Docker Linux容器开发一个.NET核心应用程序,但部署在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")

从.NET6开始,终于有可能了。以跨平台的方式处理时区,因此不再需要这些手动解决方法。

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中才能正常工作。