2012-10-24 10 views
14

Używam NodaTime w aplikacji i potrzebuję, aby użytkownik wybrał ich strefę czasową z listy rozwijanej. Mam następujące miękkie wymagania:Jak powinienem wypełnić listę stref czasowych IANA/Olson z Noda Time?

1) Lista zawiera tylko te wybory, które są uzasadnione dla obecnej i najbliższej przyszłości dla rzeczywistych miejsc. Historyczne, niejasne i ogólne strefy czasowe powinny zostać odfiltrowane.

2) Listę należy posortować najpierw według przesunięcia UTC, a następnie według nazwy strefy czasowej. Miejmy nadzieję, że postawi je w kolejności, która ma znaczenie dla użytkownika.

Napisałem poniższy kod, który rzeczywiście działa, ale nie ma dokładnie tego, czego szukam. Filtr prawdopodobnie wymaga korekty, a wolałbym, aby przesunięcie reprezentowało przesunięcie bazowe (inne niż dst), a nie bieżące przesunięcie.

Sugestie? Zalecenia?

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

Odpowiedz

26

Noda Czas 1.1 ma the zone.tab data, więc można teraz wykonać następujące czynności:

/// <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); 
} 

Alternatywnym podejściem

Zamiast zapewnienia spadku w dół w ogóle, można użyć map-based timezone picker.

enter image description here

3

Uzyskanie standardowego przesunięcia jest łatwe - tz.GetZoneInterval(now).StandardOffset. To da ci "aktualne" standardowe przesunięcie (możliwe jest, aby strefa zmieniała się z upływem czasu).

Filtrowanie może być odpowiednie dla Ciebie - nie chciałbym powiedzieć na pewno. Z pewnością nie jest to idealne w tym, że identyfikatory nie są tak naprawdę zaprojektowane do wyświetlania. Najlepiej byłoby użyć miejsca "przykładowego" z Unicode CLDR, ale w tej chwili nie mamy żadnej integracji z CLDR.

+0

Czy jest jakiś czysty sposób odfiltrować strefy czasowe historycznych? Baza danych TZ ma daty Od i Do, dla których strefy czasowe są ważne. Czy Noda w jakikolwiek sposób to ujawnia? –

+0

@MattJohnson: Nie - który fragment TZDB to ujawnia? Muszę przyznać, że nigdy w pełni nie zrozumiałem formatu, ale jeśli jest coś, co możemy dodać, możemy to zrobić (choć prawdopodobnie nie dla wersji 1.0). (Od i Do są zwykle określonymi regułami w strefie czasowej, i myślałem, że ostateczna reguła zawsze miała wartość To z "maksimum".) –

+0

Natknąłem się na tę stronę http://69.36.11.139/tzdb/tz-how- to.html, który dość dobrze wyjaśnia rzeczy. Wpisy reguł wydają się mieć dane, których potrzebuję. –

Powiązane problemy