我正在使用这段代码将“东部时区”转换为“ EST”。现在显示“ EDT”。您看不到经常在某些地方使用的缩写,并且希望坚持使用“ EST”。我该如何使用NodaTime?

 public static string GetTimeZoneAbbr(string timeZone)
        {

            var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone);

            if (timeZoneInfo != null)
            {
                var dateTime = DateTime.UtcNow;
                var instant = Instant.FromDateTimeUtc(dateTime);
                var tzdbSource = TzdbDateTimeZoneSource.Default;
                var tzid = tzdbSource.MapTimeZoneId(timeZoneInfo);
                var dateTimeZone = DateTimeZoneProviders.Tzdb[tzid];
                var zoneInterval = dateTimeZone.GetZoneInterval(instant);
                return zoneInterval.Name;
            }

            return string.Empty;
        }

最佳答案

更新

下面的答案描述了如何解析和使用CLDR数据。很好,但是通过将它们全部包含在库中,我变得更加容易。请参见this StackOverflow answerread my blog post,并查看the TimeZoneNames library。使用此库比自己解析CLDR数据容易得多。

// You can pass either type of time zone identifier:
var tz = "America/New_York";       // IANA
var tz = "Eastern Standard Time";  // Windows

// You can get names or abbreviations for any language or locale
var names = TZNames.GetNamesForTimeZone(tz, "en-US");
var abbreviations = TZNames.GetAbbreviationsForTimeZone(tz, "en-US");

names.Generic == "Eastern Time"
names.Standard == "Eastern Standard Time"
names.Daylight == "Eastern Daylight Time"

abbreviations.Generic == "ET"
abbreviations.Standard == "EST"
abbreviations.Daylight == "EDT"


原始答案

我已经在问题注释中写了一些关于为什么显示缩写形式完全有效的问题,但请允许我也回答提出的问题。

以另一种方式重申您的问题,您希望以Microsoft Windows时区ID开头,并以人类可读的字符串结尾,该字符串代表整个时区,而不仅仅是有效的时区段。

您可以给他们TimeZoneInfo.DisplayName,但这并不总是合适的。对于美国,您可能会获得一个显示名称"(UTC-05:00) Eastern Time (US & Canada),并且可以去除前导偏移量和括号以仅返回"Eastern Time (US & Canada)"。但这并不能在所有时区都起作用,因为许多时区仅具有列出城市的显示名称,例如"(UTC-04:00) Georgetown, La Paz, Manaus, San Juan"

更好的方法是使用Unicode CLDR Project中的数据。 Noda Time拥有一部分数据,但不是解决此特定问题所需的一切。因此,我无法为您提供使用Noda Time的代码示例。但是,您可以对原始CLDR数据使用以下步骤来实现您的目标:


查找与Windows时区相对应的IANA时区ID,例如您在上面的代码中已经完成的操作,或者直接使用CLDR Windows time zone mappings
CLDR MetaZones file中查找IANA时区。
在CLDR转换data filescharts such as this one之一中查找MetaZone。使用"generic-long""generic-short"模式以及您选择的语言,例如"en"用于英语。


因此,就您而言,从Windows TimeZoneInfo.Id"Eastern Standard Time"开始:


IANA区域= "America/New_York"
CLDR MetaZone = "America_Eastern"
generic-long [en] = "Eastern Time"

generic-short [en] = "ET"


请注意,并非每个Windows时区都可映射到IANA区域,也不是每个元区域都具有短名称,并且某些从未遵循夏时制的区域将仅具有标准名称,而不是通用名称。

这是一些C#代码,该代码显示如何遍历CLDR的XML数据以获得TimeZoneInfo对象的通用长名称。假定您可以在指定的路径上访问CLDR数据。下载the latest core.zip并解压缩,然后将basePath指向该文件夹。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;

static Dictionary<TimeZoneInfo, string> GetCldrGenericLongNames(string basePath, string language)
{
    // Set some file paths
    string winZonePath = basePath + @"\common\supplemental\windowsZones.xml";
    string metaZonePath = basePath + @"\common\supplemental\metaZones.xml";
    string langDataPath = basePath + @"\common\main\" + language + ".xml";

    // Make sure the files exist
    if (!File.Exists(winZonePath) || !File.Exists(metaZonePath) || !File.Exists(langDataPath))
    {
        throw new FileNotFoundException("Could not find CLDR files with language '" + language + "'.");
    }

    // Load the data files
    var xmlWinZones = XDocument.Load(winZonePath);
    var xmlMetaZones = XDocument.Load(metaZonePath);
    var xmlLangData = XDocument.Load(langDataPath);

    // Prepare the results dictionary
    var results = new Dictionary<TimeZoneInfo, string>();

    // Loop for each Windows time zone
    foreach (var timeZoneInfo in TimeZoneInfo.GetSystemTimeZones())
    {
        // Get the IANA zone from the Windows zone
        string pathToMapZone = "/supplementalData/windowsZones/mapTimezones/mapZone" +
                               "[@territory='001' and @other='" + timeZoneInfo.Id + "']";
        var mapZoneNode = xmlWinZones.XPathSelectElement(pathToMapZone);
        if (mapZoneNode == null) continue;
        string primaryIanaZone = mapZoneNode.Attribute("type").Value;

        // Get the MetaZone from the IANA zone
        string pathToMetaZone = "/supplementalData/metaZones/metazoneInfo/timezone[@type='" + primaryIanaZone +  "']/usesMetazone";
        var metaZoneNode = xmlMetaZones.XPathSelectElements(pathToMetaZone).LastOrDefault();
        if (metaZoneNode == null) continue;
        string metaZone = metaZoneNode.Attribute("mzone").Value;

        // Get the generic name for the MetaZone
        string pathToNames = "/ldml/dates/timeZoneNames/metazone[@type='" + metaZone + "']/long";
        var nameNodes = xmlLangData.XPathSelectElement(pathToNames);
        var genericNameNode = nameNodes.Element("generic");
        var standardNameNode = nameNodes.Element("standard");
        string name = genericNameNode != null
            ? genericNameNode.Value
            : standardNameNode != null
                ? standardNameNode.Value
                : null;

        // If we have valid results, add to the dictionary
        if (name != null)
        {
            results.Add(timeZoneInfo, name);
        }
    }

    return results;
}


调用此命令将为您提供字典,然后可以将其用于查找。例:

// load the data once an cache it in a static variable
const string basePath = @"C:\path\to\extracted\cldr\core";
private static readonly Dictionary<TimeZoneInfo, string> timeZoneNames =
    GetCldrGenericLongNames(basePath, "en");

// then later access it like this
string tzname = timeZoneNames[yourTimeZoneInfoObject];

关于c# - TimeZone转换显示EDT而不是EST,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/22890184/

10-10 18:17