63

TimeZoneInfo不提供给定时区的缩写或短名称。唯一的好方法是拥有一个字典,将缩写映射到Timezone.id,StandardNameDaylightName属性。但是,我在缩写列表中搜索的所有来源都有不同的时区名称,即与 Windows 中的不同。

如何在 .NET 中向用户显示不是全名、ID 或任何其他名称的用户?我不想要 UtcOffset 但时区缩写 - PST 太平洋,UTC - 通用,EST - 东部标准等。是否有任何 C# 兼容列表或数据库与所有可能的时区及其缩写兼容那些TimeZoneInfo.GetSystemTimeZones()给你?

4

7 回答 7

80

更新的答案

我的原始回复如下,并且仍然有效。但是,现在有一种更简单的方法,即使用TimeZoneNames 库从 Nuget安装后,您可以执行以下操作:

string tzid = theTimeZoneInfo.Id;                // example: "Eastern Standard time"
string lang = CultureInfo.CurrentCulture.Name;   // example: "en-US"
var abbreviations = TZNames.GetAbbreviationsForTimeZone(tzid, lang);

生成的对象将具有类似于以下内容的属性:

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

您还可以使用同一个库来获取完全本地化的时区名称。该库使用 CLDR 数据的嵌入式独立副本。

原始答案

正如其他人所提到的,时区缩写是模棱两可的。但如果你真的想要一个用于显示,你需要一个 IANA/Olson 时区数据库。

您可以从 Windows 时区转到 IANA/Olson 时区以及其他方向。但请注意,任何给定的 Windows 区域都可能有多个 IANA/Olson 区域。这些映射在此处的 CLDR 中维护。

NodaTime同时拥有数据库和映射。您可以从 .NetDateTimeDateTimeOffset,TimeZoneInfo转到 NodaTimeInstantDateTimeZone. 从那里,您可以获得缩写名称。

// starting with a .Net TimeZoneInfo
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");

// You need to resolve to a specific instant in time - a noda Instant
// For illustrative purposes, I'll start from a regular .Net UTC DateTime
var dateTime = DateTime.UtcNow;
var instant = Instant.FromDateTimeUtc(dateTime);

// note that if we really wanted to just get the current instant,
// it's better and easier to use the following:
// var instant = SystemClock.Instance.Now;


// Now let's map the Windows time zone to an IANA/Olson time zone,
// using the CLDR mappings embedded in NodaTime.  This will use
// the *primary* mapping from the CLDR - that is, the ones marked
// as "territory 001".

// we need the NodaTime tzdb source.  In NodaTime 1.1.0+:
var tzdbSource = TzdbDateTimeZoneSource.Default;

// in previous NodaTime releases:
// var tzdbSource = new TzdbDateTimeZoneSource("NodaTime.TimeZones.Tzdb");

// map to the appropriate IANA/Olson tzid
var tzid = tzdbSource.MapTimeZoneId(timeZoneInfo);
        
// get a DateTimeZone from that id
var dateTimeZone = DateTimeZoneProviders.Tzdb[tzid];


// Finally, let's figure out what the abbreviation is
// for the instant and zone we have.

// now get a ZoneInterval for the zone and the instant
var zoneInterval = dateTimeZone.GetZoneInterval(instant);

// finally, you can get the correct time zone abbreviation
var abbreviation = zoneInterval.Name;

// abbreviation will be either PST or PDT depending
// on what instant was provided
Debug.WriteLine(abbreviation);
于 2013-03-08T22:25:40.140 回答
15

这是一个棘手的要求,您能做的最好的事情就是获取您选择的列表并创建一个扩展/帮助方法来获取给定 TimeZoneInfo 的缩写。

一旦开始的地方是http://www.timeanddate.com/library/abbreviations/timezones/它有一个版本的列表,涵盖了我知道的区域。

问题在于在给定时区存在多个缩写的情况下选择适当的缩写。例如, UTC可以表示为UTCWET(西欧时间)WEZ(Westeuropäische Zeit)WT(西撒哈拉标准时间)

您可能希望与您的利益相关者就您将遵循给定选择的命名约定达成一致。

于 2013-03-08T21:22:09.873 回答
13

您的问题并未表明您的应用程序必须在哪些时区运行,但在我的特定情况下,我只需要关注美国时区和 UTC。

美国时区的缩写总是时区名称中每个单词的第一个字符。例如,“Mountain Standard Time”的缩写是“MST”,“Eastern Daylight Time”的缩写是“EDT”。

如果你有类似的需求,可以直接从本地时区的名称中轻松推导出本地时区的时区缩写,如下(注意:这里我是根据当前的日期和时间来确定正确的名称):

string timeZoneName = TimeZone.CurrentTimeZone.IsDaylightSavingTime(DateTime.Now)
                          ? TimeZone.CurrentTimeZone.DaylightName 
                          : TimeZone.CurrentTimeZone.StandardName;

string timeZoneAbbrev = GetTzAbbreviation(timeZoneName);

GetTzInitials()函数的代码非常简单。值得一提的是,某些时区可能设置为墨西哥或加拿大,这些时区的名称会在括号中加上国家名称,例如“太平洋标准时间(墨西哥)”。为了解决这个问题,任何带括号的数据都被直接传回。上面返回的缩写将是“PST(Mexico)”,这对我有用。

string GetTzAbbreviation(string timeZoneName) {
    string output = string.Empty;

    string[] timeZoneWords = timeZoneName.Split(' ');
    foreach (string timeZoneWord in timeZoneWords) {
        if (timeZoneWord[0] != '(') {
            output += timeZoneWord[0];
        } else {
            output += timeZoneWord;
        }
    }
    return output;
}
于 2014-03-29T16:02:01.117 回答
4

我将所有日期都存储在 UTC 中,并且经常必须在本地时间显示它们,所以我创建了一个扩展方法ToAbbreviation()

    public static string ToAbbreviation(this TimeZone theTimeZone)
    {

        string timeZoneString = theTimeZone.StandardName;
        string result = string.Concat(System.Text.RegularExpressions.Regex
           .Matches(timeZoneString, "[A-Z]")
           .OfType<System.Text.RegularExpressions.Match>()
           .Select(match => match.Value));

        return result;
    }

示例用法:

string tz = TimeZone.CurrentTimeZone.ToAbbreviation();
string formattedDate = String.Format("{0:yyyy/MM/dd hh:mm:ss} {1}", myDate, tz);

或者,如果您只想从 DateTime 对象中获取格式化的日期字符串:

public static string ToLocalTimeWithTimeZoneAbbreviation(this DateTime dt)
{
    DateTime localtime = dt.ToLocalTime();
    string tz = TimeZone.CurrentTimeZone.ToAbbreviation();

    string formattedDate = String.Format("{0:yyyy/MM/dd hh:mm:ss} {1}", localtime, tz);
    return formattedDate;
}

并使用如下:

string formattedDate = myDateTimeObject.ToLocalTimeWithTimeZoneAbbreviation()

输出:2019-06-24 02:26:31 EST

于 2019-07-09T14:41:24.033 回答
3

这是另一个使用 NodaTime 的片段:

NodaTime.ZonedDateTime hereAndNow = NodaTime.SystemClock.Instance.Now.InZone(
    NodaTime.DateTimeZoneProviders.Tzdb.GetSystemDefault());

System.TimeSpan zoneOffset = hereAndNow.ToDateTimeOffset().Offset;

string sTimeDisplay = string.Format("{0:G} {1} (UTC{2}{3:hh\\:mm} {4})", 
    hereAndNow.ToDateTimeOffset(), 
    hereAndNow.Zone.GetZoneInterval(hereAndNow.ToInstant()).Name, 
    zoneOffset < TimeSpan.Zero ? "-" : "+", 
    zoneOffset, 
    hereAndNow.Zone.Id);

在我的系统上,这会产生:“4/11/2013 5:03:23 PM CDT (UTC-05:00 America/Chicago)”

(感谢马特·约翰逊(Matt Johnson)对缩写词存在于 TimeZoneInterval 中的线索的回答)

如果 NodaTime.ZonedDateTime 有一个 GetZoneInterval 方法会更容易,但也许我错过了一些东西。

于 2013-04-11T22:20:08.250 回答
2

这对使用 Xamarin for iOS 或 Android 的任何人都有帮助,因为根据文档“显示名称是根据随 Windows 操作系统安装的区域性进行本地化的”。在中央时区使用它时,对于 DateTime“2016-09-01 12:00:00 GMT”,此函数返回“CDT”,这正是我遇到这个问题时所需要的。

public static string GetTimeZoneAbbreviation(DateTime time)
{
    string timeZoneAbbr;
    if(time.IsDaylightSavingTime() == true)
    {
        timeZoneAbbr = TimeZoneInfo.Local.DaylightName;
    }
    else
    {
        timeZoneAbbr = TimeZoneInfo.Local.StandardName;
    }

    return timeZoneAbbr;
}
于 2016-07-14T14:40:52.830 回答
1

我有一个类似的问题,但不想使用 3rd 方库(正如大多数答案所暗示的那样)。如果您只想支持 .Net 的时区名称,并决定使用查找表,请在此处查看我的答案,它依靠 PHP 来帮助生成缩写列表。您可以调整那里的代码段和表格以满足您的需要。

于 2016-03-22T18:42:29.350 回答