2013-02-19 10 views
9

Używam NodaTime ze względu na jego ładne wsparcie dla danych zoneinfo, jednak mam przypadek, w którym muszę przekonwertować DateTimeZone na TimeZoneInfo do wykorzystania w Quartz.NET.Konwertuj NodaTime DateTimeZone na TimeZoneInfo

Jakie jest zalecane podejście? IANA ma plik mapujący między strefami czasowymi Windows i strefami czasowymi zoneinfo. Czy mogę utworzyć metodę rozszerzenia wykorzystującą te informacje?

Dzięki Dean

+0

Który plik IANA ma na myśli, przy okazji? Wiem, że istnieje CLDR, ale jeśli IANA zapewnia to w innym formacie, byłoby to interesujące ... –

Odpowiedz

12

Chciałbym unikać refleksji, jeśli to możliwe. Nie chciałbym postawić na twoje podejście do pracy z przyszłymi wersjami :)

Możesz przesłać prośbę o dodanie tej funkcji dla przyszłych wersji, ale na razie rozbudowałem Twój słownik odwrotny w bardziej stabilny sposób sposób:

// Note: this version lets you work with any IDateTimeZoneSource, although as the only 
// other built-in source is BclDateTimeZoneSource, that may be less useful :) 
private static IDictionary<string, string> LoadTimeZoneMap(IDateTimeZoneSource source) 
{ 
    var nodaToWindowsMap = new Dictionary<string, string>(); 
    foreach (var bclZone in TimeZoneInfo.GetSystemTimeZones()) 
    { 
     var nodaId = source.MapTimeZoneId(bclZone); 
     if (nodaId != null) 
     { 
      nodaToWindowsMap[nodaId] = bclZone.Id; 
     } 
    } 
    return nodaToWindowsMap; 
} 

oczywiście, to nie obejmie wszystkie strefy czasowe w TZDB. W rzeczywistości nie będzie nawet podawać wszystkich informacji, które możemy przekazać na podstawie informacji CLDR, których używamy ... CLDR daje wiele odwzorowań dla każdego identyfikatora Windows, a my przechowujemy tylko ten pierwszy w danym momencie. Próbowaliśmy dowiedzieć się, jak to ujawnić, ale jeszcze nie udało się. Myśli są mile widziane na liście mailingowej Noda Time :)

Należy również pamiętać, że tylko dlatego, że istnieje mapowanie pomiędzy strefami BCL i TZDB, nie oznacza to, że faktycznie dają one identyczne wyniki dla wszystkich - to tylko najbliższe mapowanie dostępne.

+0

Dzięki Jon , takie podejście jest znacznie lepsze.Uwzględniono twoje uwagi na temat możliwości różnicowania danych między TZDB i CLDR. Zrobiliśmy listę stref czasowych potrzebnych naszym urządzeniom użytkowników końcowych i mają one w danym momencie bezpośrednie mapowanie do prawidłowych stref czasowych systemu Windows. To możliwe, że musimy wymyślić inne podejście, ale to działa na razie. –

+0

@DeanWard: Jeśli to jakaś pomoc, już dziś zaczęłam implementować [wydanie 82] (https://code.google.com/p/noda-time/issues/detail?id=82), aby uzyskać więcej informacji na temat mapowania. Nie wiem na pewno, czy uda się to w wersji 1.1. –

+0

Będę miał na to oko, dzięki za twoją pomoc! –

4

Aha, znalazłem go - TzdbDateTimeZoneSource ma MapTimeZoneId sposób, że mogę wpaść do TimeZoneInfo.FindSystemTimeZoneById.

Edit: MapTimeZoneId robi mapowanie od strefy czasowej systemu Windows do zoneinfo ... skończyło się uciekania się do refleksji, aby zrobić mapowanie w przeciwnym kierunku:

using System; 
using System.Collections.Generic; 
using System.Reflection; 

using NodaTime; 
using NodaTime.TimeZones; 

/// <summary> 
/// Extension methods for <see cref="DateTimeZone" />. 
/// </summary> 
internal static class DateTimeZoneExtensions 
{ 
    private static readonly Lazy<IDictionary<string, string>> map = new Lazy<IDictionary<string, string>>(LoadTimeZoneMap, true); 

    public static TimeZoneInfo ToTimeZoneInfo(this DateTimeZone timeZone) 
    { 
     string id; 
     if (!map.Value.TryGetValue(timeZone.Id, out id)) 
     { 
      throw new TimeZoneNotFoundException(string.Format("Could not locate time zone with identifier {0}", timeZone.Id)); 
     } 

     TimeZoneInfo timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(id); 
     if (timeZoneInfo == null) 
     { 
      throw new TimeZoneNotFoundException(string.Format("Could not locate time zone with identifier {0}", timeZone.Id)); 
     } 

     return timeZoneInfo; 
    } 

    private static IDictionary<string, string> LoadTimeZoneMap() 
    { 
     TzdbDateTimeZoneSource source = new TzdbDateTimeZoneSource("NodaTime.TimeZones.Tzdb"); 
     FieldInfo field = source.GetType().GetField("windowsIdMap", BindingFlags.Instance | BindingFlags.NonPublic); 
     IDictionary<string, string> map = (IDictionary<string, string>)field.GetValue(source); 

     // reverse the mappings 
     Dictionary<string, string> reverseMap = new Dictionary<string, string>(); 
     foreach (KeyValuePair<string, string> kvp in map) 
     { 
      reverseMap.Add(kvp.Value, kvp.Key); 
     } 

     return reverseMap; 
    } 
} 
+1

Pomoże to, jeśli nie chcesz, aby nowe źródło było nowe: NodaTime.TimeZones.TzdbDateTimeZoneSource.Default – jsgoupil

0

Jednak nic z tego nie działa w PCL, ponieważ większość pracy jest wykonywana przez .NET w metodach .GetSystemTImeZones() i .FindSystemTIemZoneById() - które nie istnieją w PCL.

Jestem oszołomiony, że za wszystkie informacje, które można uzyskać z NodaTime, uzyskanie czegoś tak prostego jak skrót "EST", gdy masz już nazwę strefy "US/Eastern", prawdopodobnie zatrzymało mnie w moich śladach .

1

Możesz użyć biblioteki TimeZoneConverter przez Matt Johnson.

ZoneId wykorzystywane przez NodeTime TzdbZoneLocation jest IANA time zone, dzięki czemu można uzyskać TimeZoneInfo tak:

string windowsTimeZoneName = TZConvert.IanaToWindows(tzdbZoneLocation.ZoneId); 
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(windowsTimeZoneName); 

Nie zapomnij owinąć go z try-catch z jakiejś awaryjnej na wszelki wypadek.

Zobacz także original Matt Johnson solution do konwersji strefy czasowej IANA i strefy czasowej Windows.