如何在Windows和IANA时区之间进行翻译?
如时区标记wiki中所述 ,有两种不同的时区样式。
-
由Microsoft提供的用于Windows和.Net
TimeZoneInfo
类的值由诸如Eastern Standard Time
类的值标识。 -
IANA在TZDB中提供的数据通过诸如
America/New_York
的值来确定。
许多基于互联网的API使用IANA时区,但出于多种原因,可能需要将其转换为Windows时区ID,反之亦然。
这怎么能在.Net中完成呢?
- 如何等待,直到远程.NETdebugging器连接
- 在wpf中禁用项目控制上的鼠标滚轮
- 关联性math:(a + b)+ c!= a +(b + c)
- 获取Enum的值的属性
- 使用Windows服务和c#检测USB驱动器插入和删除
Windows和IANA时区标识符之间转换数据的主要来源是Unicode CLDR的一部分。
在大多数情况下,IANA时区可映射到单个Windows时区。 但相反是不正确的。 单个Windows时区可能会映射到多个IANA时区。
更新解决方案
现在,使用我的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"
项目网站上的更多示例
原始解决方案(从2013年起 – 仍然有效)
除了作为高级日期时间API之外, Noda时间库还包含CLDR映射的嵌入副本。
以下功能可用于转换:
// This will return the Windows zone that matches the IANA zone, if one exists. public string IanaToWindows(string ianaZoneId) { var utcZones = new[] { "Etc/UTC", "Etc/UCT", "Etc/GMT" }; if (utcZones.Contains(ianaZoneId, StringComparer.Ordinal)) return "UTC"; var tzdbSource = NodaTime.TimeZones.TzdbDateTimeZoneSource.Default; // resolve any link, since the CLDR doesn't necessarily use canonical IDs var links = tzdbSource.CanonicalIdMap .Where(x => x.Value.Equals(ianaZoneId, StringComparison.Ordinal)) .Select(x => x.Key); // resolve canonical zones, and include original zone as well var possibleZones = tzdbSource.CanonicalIdMap.ContainsKey(ianaZoneId) ? links.Concat(new[] {tzdbSource.CanonicalIdMap[ianaZoneId], ianaZoneId}) : links; // map the windows zone var mappings = tzdbSource.WindowsMapping.MapZones; var item = mappings.FirstOrDefault(x => x.TzdbIds.Any(possibleZones.Contains)); if (item == null) return null; return item.WindowsId; } // This will return the "primary" IANA zone that matches the given windows zone. // If the primary zone is a link, it then resolves it to the canonical ID. public string WindowsToIana(string windowsZoneId) { if (windowsZoneId.Equals("UTC", StringComparison.Ordinal)) return "Etc/UTC"; var tzdbSource = NodaTime.TimeZones.TzdbDateTimeZoneSource.Default; var tzi = TimeZoneInfo.FindSystemTimeZoneById(windowsZoneId); if (tzi == null) return null; var tzid = tzdbSource.MapTimeZoneId(tzi); if (tzid == null) return null; return tzdbSource.CanonicalIdMap[tzid]; }
请注意,UTC的映射必须单独处理。 这是因为CLDR将UTC错误地映射为"Etc/GMT"
,并且"Etc/GMT"
未在tzdb中设置为"Etc/UTC"
的链接。 更多关于这里。