C# 时区转换显示EDT而不是EST

C# 时区转换显示EDT而不是EST,c#,.net,time,timezone,nodatime,C#,.net,Time,Timezone,Nodatime,我使用这段代码将“东部时区”转换为“东部时间”。现在正在播放“EDT”。你看不到缩写经常出现在某些地方,你会坚持使用“EST”。我如何使用NodaTime来实现这一点 public static string GetTimeZoneAbbr(string timeZone) { var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone); if (timeZ

我使用这段代码将“东部时区”转换为“东部时间”。现在正在播放“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数据。这很好,但我将这些都包含在一个库中,从而使它变得更容易。看,看一看。使用这个库比自己解析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)东部时间(美国和加拿大)
,您可以去掉前导偏移量和括号,只返回
“东部时间(美国和加拿大)”
。但这并不适用于所有时区,因为许多时区的显示名称只列出城市,例如
(UTC-04:00)乔治敦、拉巴斯、马瑙斯、圣胡安“

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

  • 查找与Windows时区对应的IANA时区ID,如您在上面的代码中已经完成的,或者直接使用

  • 在中查找IANA时区

  • 在一个CLDR翻译中查找元区域,或。使用
    “generic long”
    “generic short”
    模式,以及您选择的语言,例如英语为
    “en”

  • 因此,在您的例子中,从“东部标准时间”的Windows
    TimeZoneInfo.Id
    开始:

  • IANA区域=
    “美国/纽约”

  • CLDR MetaZone=
    “美国东部”

  • 通用长[en]=
    “东部时间”

    通用短[en]=
    “ET”

  • 请注意,并不是每个Windows时区都可以映射到IANA时区,也不是每个元时区都有一个短名称,一些从未遵循夏令时的时区将只有一个标准名称,而不是通用名称

    下面是一些C#代码,显示了如何遍历CLDR的XML数据以获得
    TimeZoneInfo
    对象的通用长名称。它假设您可以在指定的路径访问CLDR数据。下载并提取,然后将
    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;
    }
    
    使用系统;
    使用System.Collections.Generic;
    使用System.IO;
    使用System.Linq;
    使用System.Xml.Linq;
    使用System.Xml.XPath;
    静态字典GetCldrGenericLongNames(字符串基路径,字符串语言)
    {
    //设置一些文件路径
    字符串winZonePath=basePath+@“\common\supplemental\windowsZones.xml”;
    字符串metaZonePath=basePath+@“\common\supplemental\metaZones.xml”;
    字符串langDataPath=basePath+@“\common\main\”+language+.xml”;
    //确保文件存在
    如果(!File.Exists(winZonePath)| |!File.Exists(metaZonePath)| |!File.Exists(langDataPath))
    {
    抛出新的FileNotFoundException(“找不到语言为“+”语言为“+”的CLDR文件”);
    }
    //加载数据文件
    var xmlWinZones=XDocument.Load(winZonePath);
    var xmlmetarzones=XDocument.Load(metaZonePath);
    var xmlLangData=XDocument.Load(langDataPath);
    //准备结果字典
    var results=newdictionary();
    //每个Windows时区的循环
    foreach(timeZoneInfo.GetSystemTimeZones()中的var timeZoneInfo)
    {
    //从Windows区域获取IANA区域
    字符串pathToMapZone=“/supplementalData/windowsZones/mapTimezones/mapZone”+
    “[@territory='001'和@other='001'”+timeZoneInfo.Id+']”;
    var mapZoneNode=xmlWinZones.XPathSelectElement(pathToMapZone);
    如果(mapZoneNode==null)继续;
    字符串primaryanazone=mapZoneNode.Attribute(“type”).Value;
    //从IANA区域获取元区域
    字符串pathToMetaZone=“/supplementalData/metaZones/metazoneInfo/timezone[@type=”+primaryanazone+“”]/usesMetazone”;
    var metaZoneNode=xmlmetazines.XPathSelectElements(pathToMetaZone.LastOrDefault();
    如果(metaZoneNode==null)继续;
    字符串metaZone=metaZoneNode.Attribute(“mzone”).Value;
    //获取元区域的通用名称
    字符串pathToNames=“/ldml/dates/timeZoneNames/metazone[@type='”+metazone+']/long”;
    var nameNodes=xmlLangData.XPathSelectElement(路径名称);
    var genericNameNode=nameNodes.Element(“generic”);
    var standardNameNode=nameNodes.Element(“标准”);
    字符串名称=genericNameNode!=null
    ?genericNameNode.Value
    :standardNameNode!=null
    ?standardNameNode.Value
    :null;
    //如果我们有有效的结果,请添加到字典中
    if(name!=null)
    {
    结果.添加(timeZoneInfo,名称);
    }
    }
    返回结果;
    }
    
    调用此函数将获得一本字典,然后可以使用该字典进行查找。例如:

    // 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];
    
    //将数据缓存到静态变量中后加载数据
    常量字符串basePath=@“C:\path\to\extracted\cldr\core”;
    专用静态只读字典时区名称=
    GetCldrGener