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

2023-09-02 10:15:28 作者:辞曲

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

As described in the timezone tag wiki, there are two different styles of time zones.

对于那些使用Windows和.Net 的TimeZoneInfo 类是由一个值来识别,如东部标准时间。

Those provided by Microsoft for use with Windows and the .Net TimeZoneInfo class are identified by a value such as Eastern Standard Time.

这些都是由一个值确定了TZDB提供IANA如美国/纽约

Those provided by IANA in the TZDB are identified by a value such as America/New_York.

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

Many Internet-based APIs use the IANA time zones, but for numerous reasons one might need to convert this to a Windows time zone id, or vice-versa.

如何在.net中可能?

How can this be accomplished in .Net?

推荐答案

有关Windows和IANA时区标识符之间的转换数据的主要来源是的单向code CLDR 。

The primary source of the data for conversion between Windows and IANA time zone identifiers is part of the Unicode CLDR.

在大多数情况下,一个IANA时区可以被映射到一个单一的视窗时区。但相反的是不正确的。一个单一的Windows时区可能映射到多个IANA时区。

In most cases, an IANA time zone can be mapped to a single Windows time zone. But the reverse is not true. A single Windows time zone might be mapped to more than one IANA time zone.

在除了作为一个卓越的日期和时间的API,在野田佳彦时间库包含了CLDR映射的嵌入式副本。

In addition to being a superior date time API, the Noda Time library contains an embedded copy of the CLDR mappings.

下面的功能可以被用来转换:

The following functions can be used to convert:

// 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 / GMTETC / GMT未设置起来作为TZDB链接ETC / UTC。 更多关于此这里。

Note that the mapping for UTC has to be handled separately. This is because the CLDR erroneously maps UTC to "Etc/GMT", and "Etc/GMT" is not set up as a link to "Etc/UTC" in the tzdb. More on this here.