TimeZone转换显示EDT而不是EST

时间:2014-04-06 05:00:30

标签: c# .net time timezone nodatime

我正在使用这段代码转换"东部时区"到" 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;
        }

1 个答案:

答案 0 :(得分:4)

<强>更新

下面的答案描述了如何解析和使用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开始,最后得到一个代表整个时区的人类可读字符串,而不仅仅是有效的时区段。 / p>

可以只给他们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数据使用以下步骤来实现您的目标:

  1. 查找与Windows时区对应的IANA时区ID,例如您在上面的代码中已经完成的,或直接使用CLDR Windows time zone mappings

  2. CLDR MetaZones file

  3. 中查找IANA时区
  4. 在其中一个CLDR翻译data filescharts such as this one中查找MetaZone。使用"generic-long""generic-short"模式以及您选择的语言,例如英语"en"

  5. 因此,在您的情况下,从TimeZoneInfo.Id的Windows "Eastern Standard Time"开始:

    1. IANA区域= "America/New_York"

    2. CLDR MetaZone = "America_Eastern"

    3. generic-long [en] = "Eastern Time"

      generic-short [en] = "ET"

    4. 请注意,并非每个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];