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

如时区标记wiki中所述 ,有两种不同的时区样式。

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

  • IANA在TZDB中提供的数据通过诸如America/New_York的值来确定。

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

这怎么能在.Net中完成呢?

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"的链接。 更多关于这里。