如何从 Noda Time 填充 IANA / Olson 时区列表?

16

我正在一个应用程序中使用NodaTime,需要用户从下拉列表中选择他们的时区。 我有以下软件要求:

1)列表仅包含对于真实地点的现在和不久的未来合理有效的选择。 应过滤掉历史、模糊和通用时区。

2)该列表应首先按UTC偏移量排序,然后按时区名称排序。 这将有望按对用户有意义的顺序排列它们。

我编写了以下代码,它确实可以工作,但并不完全符合我的要求。 过滤器可能需要进行调整,我更希望偏移量代表基本(非夏令时)偏移量,而不是当前偏移量。

有建议吗? 推荐内容?

var now = Instant.FromDateTimeUtc(DateTime.UtcNow);
var tzdb = DateTimeZoneProviders.Tzdb;
var list = from id in tzdb.Ids
           where id.Contains("/") && !id.StartsWith("etc", StringComparison.OrdinalIgnoreCase)
           let tz = tzdb[id]
           let offset = tz.GetOffsetFromUtc(now)
           orderby offset, id
           select new
           {
               Id = id,
               DisplayValue = string.Format("({0}) {1}", offset.ToString("+HH:mm", null), id)
           };

// ultimately we build a dropdown list, but for demo purposes you can just dump the results
foreach (var item in list)
    Console.WriteLine(item.DisplayValue);
2个回答

26

Noda Time 1.1现在有zone.tab数据,因此您现在可以执行以下操作:

/// <summary>
/// Returns a list of valid timezones as a dictionary, where the key is
/// the timezone id, and the value can be used for display.
/// </summary>
/// <param name="countryCode">
/// The two-letter country code to get timezones for.
/// Returns all timezones if null or empty.
/// </param>
public IDictionary<string, string> GetTimeZones(string countryCode)
{
    var now = SystemClock.Instance.Now;
    var tzdb = DateTimeZoneProviders.Tzdb;

    var list = 
        from location in TzdbDateTimeZoneSource.Default.ZoneLocations
        where string.IsNullOrEmpty(countryCode) ||
              location.CountryCode.Equals(countryCode, 
                                          StringComparison.OrdinalIgnoreCase)
        let zoneId = location.ZoneId
        let tz = tzdb[zoneId]
        let offset = tz.GetZoneInterval(now).StandardOffset
        orderby offset, zoneId
        select new
        {
            Id = zoneId,
            DisplayValue = string.Format("({0:+HH:mm}) {1}", offset, zoneId)
        };

    return list.ToDictionary(x => x.Id, x => x.DisplayValue);
}

另一种方法

不需要提供下拉菜单,你可以使用基于地图的时区选择器

enter image description here


3
获取标准偏移量很容易 - tz.GetZoneInterval(now).StandardOffset。这将给出“当前”的标准偏移量(可能会随着时间而改变)。
过滤可能适合您 - 我不能确定。它确实不是理想的,因为ID并不真正设计用于显示。理想情况下,您应该使用Unicode CLDR "example"地点,但目前我们在这方面没有任何CLDR集成。

@MattJohnson:没有 - TZDB 的哪一部分暴露了这个?我必须承认我从来没有完全理解过这个格式,但如果它是我们可以添加的内容,我们可能会这样做(尽管可能不是在1.0版本中)。 (从和到通常用于时区内的特定规则,我认为最后一个规则的“到”始终为“max”。) - Jon Skeet
为了过滤数据,我想移除任何没有“max”规则但已经过期的最终日期的时区。但我假设每个区域至少有一个规则,我不确定是否总是这样。 - Matt Johnson-Pint
@MattJohnson:你能否提出一个功能请求,引用那个网页,并以檀香山为例?目前我们假设(实际上在各个地方都是如此)时区是永久的...听起来好像不应该这样。这可能会很有趣。 - Jon Skeet
@MattJohnson:太棒了,谢谢。希望能尽快推出1.0版本(真的很快)- 这可能是我要关注的第一个v1之后的功能 :) - Jon Skeet
我发现timezonedb.com的文件只是tzdata中zone.tab文件的重新打包。看起来noda目前不包括这个文件,所以我将它嵌入到我的应用程序中。我更新了问题,并附上了我最终使用的代码。 - Matt Johnson-Pint
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接