2013-02-19 10 views
9

Sto usando NodaTime per il suo buon supporto per i dati zoneinfo, tuttavia ho un caso in cui ho bisogno di convertire lo DateTimeZone in TimeZoneInfo per l'uso in Quartz.NET.Converti NodaTime DateTimeZone in TimeZoneInfo

Qual è l'approccio consigliato qui? IANA ha un file di mapping tra i fusi orari di Windows e i fusi orari Zoneinfo, posso creare un metodo di estensione che utilizza queste informazioni?

Grazie, Dean

+0

Quale file IANA intendi, tra l'altro? So che c'è un CLDR, ma se IANA fornisce questo in un formato diverso, sarebbe interessante ... –

risposta

12

vorrei evitare utilizzando la riflessione, se possibile. Non vorrei scommettere sul tuo approccio con le versioni future :)

Sentiti libero di presentare una richiesta di funzionalità per questa funzionalità per le versioni future, ma per il momento costruirò il dizionario inverso in una versione più stabile modo:

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

Naturalmente, questo non coprirà tutti i fusi orari in TZDB. Infatti, non fornirà nemmeno tutte le informazioni che possiamo fornire in base alle informazioni CLDR che usiamo ... CLDR fornisce più mapping per ogni ID di Windows e al momento memorizziamo solo il primo. Abbiamo cercato di capire come esporne di più, ma non siamo ancora riusciti. I pensieri sono benvenuti nella mailing list di Noda Time :)

Si noti inoltre che, proprio perché esiste una mappatura tra le zone BCL e TZDB, non significa che effettivamente darà gli stessi risultati per tutto: è solo la mappatura più vicina disponibile.

+0

Grazie Jon , questo approccio è molto meglio.Preso a bordo i tuoi commenti sulla possibilità di dati diversi tra TZDB e CLDR. Abbiamo preso l'elenco dei fusi orari necessari per i nostri dispositivi degli utenti finali e al momento hanno una mappatura uno-a-uno per i fusi orari di Windows validi. È possibile che sia necessario pensare a un approccio diverso, ma questo funziona per ora. –

+0

@DeanWard: se è di aiuto, ho iniziato a implementare [problema 82] (https://code.google.com/p/noda-time/issues/detail?id=82) oggi per fornire ulteriori informazioni sul mappature. Non so se riuscirà comunque a farlo nella versione 1.1. –

+0

Terrò d'occhio questo, grazie per tutto il tuo aiuto! –

4

Aha, ho trovato - TzdbDateTimeZoneSource ha un metodo MapTimeZoneId che posso pop in TimeZoneInfo.FindSystemTimeZoneById.

Edit: MapTimeZoneId fa la mappatura dal fuso orario di Windows in zoneinfo ... Ho finito per ricorrere alla riflessione per fare la mappatura nella direzione opposta:

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

Questo aiuterà se non si desidera aggiornare la sorgente: NodaTime.TimeZones.TzdbDateTimeZoneSource.Default – jsgoupil

0

Tuttavia, nessuna di queste funzioni in un PCL perché la maggior parte del lavoro viene eseguita da .NET nei metodi .GetSystemTImeZones() e .FindSystemTIemZoneById(), che non esistono in PCL.

Sono sbalordito che per tutte le informazioni che puoi ottenere da NodaTime, ottenere qualcosa di semplice come l'abbreviazione "EST" quando hai già il nome della zona di "US/Eastern" sembra che mi abbia fermato nelle mie tracce .

1

È possibile utilizzare la libreria TimeZoneConverter entro il Matt Johnson.

IDArea usata da NodeTime TzdbZoneLocation è IANA time zone, in modo da poter ottenere TimeZoneInfo in questo modo:

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

Non dimenticate di avvolgere con try-catch con una sorta di ripiego per ogni evenienza.

Vedere anche original Matt Johnson solution per la conversione tra fuso orario IANA e fuso orario Windows.

Problemi correlati