Подтвердить что ты не робот

Как мне заполнить список часовых поясов IANA/Olson от Noda Time?

Я использую NodaTime в приложении, и мне нужно, чтобы пользователь выбирал свой часовой пояс из выпадающего списка. У меня есть следующие мягкие требования:

1) Список содержит только варианты, которые разумно действительны для настоящего и ближайшего будущего для реальных мест. Исторические, неясные и общие часовые пояса должны быть отфильтрованы.

2) Список должен быть отсортирован сначала по смещению UTC, а затем по названию часового пояса. Это, надеюсь, ставит их в порядок, который имеет смысл для пользователя.

Я написал следующий код, который действительно работает, но не имеет того, что мне нужно. Возможно, фильтр необходимо отрегулировать, и я предпочел бы, чтобы смещение представляло смещение базы (не-dst), а не текущее смещение.

Предложения? Рекомендации?

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);
4b9b3361

Ответ 1

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

Ответ 2

Получить стандартное смещение легко - tz.GetZoneInterval(now).StandardOffset. Это даст вам "текущее" стандартное смещение (возможно изменение зоны со временем).

Фильтрация может быть подходящей для вас - я не хотел бы точно сказать. Это, конечно, не идеально, потому что идентификаторы не предназначены для отображения. В идеале вы должны использовать "примеры" Unicode CLDR, но на данный момент у нас нет интеграции CLDR на этом фронте.