How to translate between Windows and IANA time zones?
如时区标签wiki中所述,有两种不同的时区样式。
许多基于Internet的API使用IANA时区,但由于多种原因,可能需要将其转换为Windows时区ID,反之亦然。
如何在.Net中实现这一目标?
Windows和IANA时区标识符之间转换的主要数据源是windowsZones.xml文件,作为Unicode CLDR项目的一部分进行分发。
但是,CLDR每年只发布两次。这与Windows更新的定期节奏以及IANA时区数据库的不规则更新一起使得直接使用CLDR数据变得复杂。请记住,时区变化本身是由世界各国政府随心所欲地制定的,并非所有变更都是在充分通知的情况下进行的,以便在各自的生效日期之前进入这些发布周期。
还有一些其他边缘情况需要处理,CLDR没有严格限制,并且会不时弹出新的边缘情况。因此,我将解决方案的复杂性封装到TimeZoneConverter微库中,可以从Nuget安装。
使用这个库很简单。以下是转换的一些示例:
1 2 3 4 5 6 7 8
| 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/Toronto。 TimeZoneConverter将提供CLDR用"001"标记的那个,称为"黄金区",除非您专门提供国家/地区代码,并且该国家/地区的其他区域匹配。
注意:这个答案多年来一直在发展,因此下面的评论可能适用于当前版本,也可能不适用。查看编辑历史记录以获取详细信谢谢。
-
@JonSkeet厌倦了重复自己。以为我应该尝试一个"回答你自己的问题"风格的帖子。
-
什么是nodatime版本?
-
@shyamnathan - 这需要Noda Time版本1.1.0或更高版本。
-
转换(GMT+05:30) Chennai, Kolkata, Mumbai, New Delhi时使用此方法给出Asia/Calcutta,它应该是Asia/Kolkata。似乎TzdbDateTimeZoneSource包含旧值。
-
@AntoJSubash - 很好的观察! CLDR映射不一定遵循规范ID的链接。 (他们故意要求在设置后修复它们。)幸运的是,Noda Time拥有解决任何别名的规范ID所需的所有数据。我更新了上面的答案来做到这一点。现在您应该发现WindowsToIana("India Standard Time")返回Asia/Kolkata。
-
@MattJohnson在使用IanaToWindows方法转换Asia/Kolkata时失败了。但它适用于Asia/Calcutta这是旧名称。您已更新方法WindowsToIana但IanaToWindows也有同样的问题。其他几个不起作用的区域是America/Argentina/Buenos_Aires,America/Indiana/Indianapolis,Asia/Kathmandu。
-
@AntoJSubash - 再次,很棒的观察!我编辑了IanaToWindows方法来补偿。非常感谢!
-
@JonSkeet - 也许这些应该在Noda Time vNext?
-
@MattJohnson:我想我可能会将它们添加到WindowsMapping ...提交功能请求,当我有机会时我会仔细看看:)
-
如果时区名称是"US / Mountain",我遇到了一个问题 - 链接集合是空的。所以,当发生这种情况时,我只需恢复搜索 - 搜索键并获取值。这对我有用 - 我不知道这是不是最好的方法
-
@MattJohnson我第二次@sirrocco的观察。使用规范id就像var canonical = tzdbSource.CanonicalIdMap[ ianaZoneId ]; links = Enumerable.Repeat( canonical, 1 ).Concat( links );一样对我来说很有用。
-
@JohannesRudolph感谢您的反馈!我已相应更新了这些功能。
-
@sirrocco - 抱歉,我没有及早看到你的评论。更新了功能。谢谢!
-
@JonSkeet MattJohnson嗨,大家好,我现在需要这个,从2014年开始发现这个帖子。现在的状态是什么?任何完整的IanaToWindows方法直接在NodaTime中?谢谢!
-
@rouen:不,目前Noda Time没有新的东西 - 你可以使用这个代码。
-
@JonSkeet感谢您的信息。我对版本有点困惑。在github我可以看到1.3.x作为活动分支,在nuget有2.0.0-alpha。我应该从哪里获得最新的稳定库?
-
@rouen:使用1.3.x - 根据"alpha"标识,2.0.0还不稳定。
-
我从android / iOS中获取未转换为Windows时区的时区ID时会看到一些情况。例如,"亚洲/加沙","亚洲/希伯伦"。我错过了什么吗?任何指针都非常感谢。
-
Rohan是对的,也是"亚洲/加尔各答"和"亚洲/西贡"(均由Android应用程序返回)不会被转换。我目前在转换之前保留了我自己的这些例外字典和"翻译"(分别为"Asia / Kolkata"和"Asia / Ho_Chi_Minh"),但我希望最终在库中修复。
-
很好的解决方案,但我发现IanaToWindows非常慢,并做了一些小的改进,使它在我的机器上快了100倍以上。我试图进行编辑,但它被拒绝了。因此,如果有人会遇到类似的性能问题,那么可能的解决方案是:stackoverflow.com/review/suggested-edits/9542560
-
@sarh - 是的,我拒绝了,实际上用一个缺失的部分更新了地图。您建议的编辑可能更快,但消除了一类映射。如果速度是您的主要考虑因素,只需遍历此功能即可为所有区域进行迭代,并将结果保存/缓存到字典中。然后在运行时使用字典。
-
我的编辑目标是避免使用ContainsKey和GetByKey字典反模式并使其更快一些。我甚至认为它不会超过x100加速。如果可以通过次要代码清理获得可接受的性能,则不需要缓存。
-
这是我的最终版本修复:pastebin.com/hXGgxTFv。顺便说一下,感谢更新 - 它将未解决的Android时区数量从53减少到39。
-
谢谢我使用这个数据库以及其他一些东西来创建一个bash脚本:gist.github.com/CMCDragonkai/a53df086cce25319d7530eb4b2ca1da9
-
马特,这太棒了。非常感谢。
我知道这是一个老问题,但我有一个用例我虽然我会在这里分享,因为这是我在搜索时找到的最相关的帖子。我正在使用docker linux容器开发.NET Core应用程序,但是要在Windows服务器上进行部署。所以我只需要我的docker linux容器来支持windows时区名称。通过执行以下操作,我无需更改应用程序代码即可实现此功能:
1 2 3 4
| 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")
-
这是一个非常好的开箱即用的想法!这似乎应该没问题,只要你覆盖几个特定的??时区。请记住,美国有超过四个。要覆盖当前的50个州,您还需要添加America/Phoenix到"US Mountain Standard Time",Pacific/Honolulu到"Hawaiian Standard Time",America/Anchorage到"Alaskan Standard Time"和America/Adak到"Aleutian Standard Time"的链接。这不包括美国领土或历史差异,但会让你开始。
-
如果一个人的意图是覆盖整个世界或处理任何有效的时区标识符,我不会推荐这种方法。列表太长,太不稳定了。