2012-10-24 13 views
14

Tôi đang sử dụng NodaTime trong một ứng dụng và tôi cần người dùng chọn múi giờ của họ từ danh sách thả xuống. Tôi có các yêu cầu mềm sau:Tôi nên điền danh sách các múi giờ IANA/Olson từ Thời gian Noda như thế nào?

1) Danh sách này chỉ chứa các lựa chọn hợp lý cho tương lai hiện tại và tương lai cho những địa điểm thực. Lịch sử, tối nghĩa và múi giờ chung nên được lọc ra.

2) Danh sách phải được sắp xếp trước tiên bằng chênh lệch UTC và sau đó theo tên múi giờ. Điều này hy vọng đặt chúng theo thứ tự có ý nghĩa đối với người dùng.

Tôi đã viết mã sau đây, thực sự hoạt động nhưng không có chính xác những gì tôi đang theo dõi. Bộ lọc có thể cần phải được điều chỉnh, và tôi muốn có bù đắp đại diện cho cơ sở (non-dst) bù đắp, chứ không phải là bù đắp hiện tại.

Đề xuất? Khuyến nghị?

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

Trả lời

26

Noda Time 1.1 có the zone.tab data, vì vậy bây giờ bạn có thể làm như sau:

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

cách tiếp cận khác

Thay vì cung cấp một danh sách thả xuống ở tất cả, bạn có thể sử dụng một map-based timezone picker.

enter image description here

3

Lấy bù tiêu chuẩn dễ dàng - tz.GetZoneInterval(now).StandardOffset. Điều đó sẽ cung cấp cho bạn bù đắp chuẩn "hiện tại" (có thể cho một khu vực thay đổi theo thời gian).

Bộ lọc có thể phù hợp với bạn - Tôi không muốn nói chắc chắn. Chắc chắn không phải là lý tưởng là trong đó các ID không thực sự được thiết kế để hiển thị. Lý tưởng nhất là bạn nên sử dụng các địa chỉ "ví dụ" của CLDR Unicode, nhưng chúng tôi không có bất kỳ tích hợp CLDR nào trên mặt trận đó vào lúc này.

+0

Có cách nào sạch để lọc ra múi giờ lịch sử không? Cơ sở dữ liệu TZ không có Từ và Đến ngày mà múi giờ có hiệu lực. Noda có phơi bày điều này theo bất kỳ cách nào không? –

+0

@MattJohnson: Không - bit nào của TZDB phơi bày điều đó? Tôi phải thú nhận rằng tôi chưa bao giờ hiểu đầy đủ về định dạng, nhưng nếu đó là thứ chúng tôi có thể thêm vào, chúng tôi có thể làm như vậy (mặc dù có thể không phải cho 1.0). (Từ và Tới thường dành cho các quy tắc cụ thể trong một múi giờ, và tôi nghĩ quy tắc cuối cùng luôn luôn có một To of "max".) –

+0

Tôi stumbled khi trang này http://69.36.11.139/tzdb/tz-how- to.html giải thích mọi thứ khá tốt. Các mục Rule dường như có dữ liệu tôi cần. –