Как мне заполнить список часовых поясов 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);
Ответы
Ответ 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 на этом фронте.