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

Mat*_*int 138 .net c# timezone nodatime

时区标签wiki中所述,有两种不同的时区样式.

  • Microsoft提供的用于Windows和.Net TimeZoneInfo类的那些由诸如的值标识"Eastern Standard Time".

  • IANA在TZDB中提供的内容由诸如此类的值标识TimeZoneInfo.

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

如何在.Net中实现这一目标?

Mat*_*int 176

Windows和IANA时区标识符之间转换的主要数据源是windowsZones.xml作为Unicode CLDR项目的一部分分发的文件.

但是,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"
Run Code Online (Sandbox Code Playgroud)

项目网站上有更多示例.

重要的是要认识到,虽然IANA时区可以映射到单个Windows时区,但事实并非如此.单个Windows时区可能会映射到多个IANA时区.这可以在上面的例子中看到,其中Eastern Standard Time映射到两者America/New_YorkAmerica/Toronto.TimeZoneConverter将提供CLDR标记的那个"001",称为"黄金区",除非您专门提供国家代码并且该国家/地区的其他区域匹配.

注意:这个答案多年来一直在发展,因此下面的评论可能适用于当前版本,也可能不适用.查看编辑历史记录以获取详细信 谢谢.

  • @MattJohnson我第二次@sirrocco的观察.使用规范id也像`var canonical = tzdbSource.CanonicalIdMap [ianaZoneId]; links = Enumerable.Repeat(canonical,1).Concat(links);`为我做了诀窍. (2认同)
  • @sirrocco - 抱歉,我没有及早看到你的评论.更新了功能.谢谢! (2认同)

Tob*_*s J 25

从 .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");
Run Code Online (Sandbox Code Playgroud)

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


Eve*_*ent 5

我知道这是一个老问题,但我有一个用例,我想在这里分享,因为这是我在搜索时找到的最相关的帖子。我正在使用 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"
Run Code Online (Sandbox Code Playgroud)

然后,在我的 .NET 代码中,以下内容无需任何修改即可运行:TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")

  • 如果一个人的意图是覆盖整个世界或处理任何有效的时区标识符,我不会推荐这种方法。这份清单太长而且太不稳定。 (2认同)