2013-03-04 15 views
6

Quello che voglio ottenere è convertire un DateTime (analizzato da una stringa assunta in EST/EDT) in UTC. Sto usando NodaTime perché ho bisogno di usare i fusi orari Olson.Utilizzo di NodaTime per convertire i valori datetime non validi (saltati) in UTC

Conversione di un DateTime non valido (ignorato) in UTC utilizzando ZonePlocalMappingResolver di NodaTime non converte la parte minuti e secondi dell'input perché ho configurato CustomResolver per restituire l'inizio dell'intervallo dopo il divario. NodaTime non sembra avere un equivalente di TimeZoneInfo.IsInvalidTime.

Come utilizzare NodaTime per convertire i valori datetime saltati in UTC e far corrispondere il risultato del metodo GetUtc() nella classe Utils di seguito? (Metodo Utils.GetUtc utilizza System.TimeZoneInfo non NodaTime)

Questo è il banco di prova:

[TestMethod] 
public void Test_Invalid_Date() 
{ 
    var ts = new DateTime(2013, 3, 10, 2, 15, 45); 

    // Convert to UTC using System.TimeZoneInfo 
    var utc = Utils.GetUtc(ts).ToString(Utils.Format); 

    // Convert to UTC using NodaTime (Tzdb/Olson dataabase) 
    var utcNodaTime = Utils.GetUtcTz(ts).ToString(Utils.Format); 

    Assert.AreEqual(utc, utcNodaTime); 
} 

Questo è quello che sto ottenendo:

Assert.AreEqual fallito. Previsto: < 2013-03-10 07: 15: 45.000000>. Effettivo: < 2013-03-10 07: 00: 00.000000>.

Ecco la classe Utils (anche su github):

using System; 

using NodaTime; 
using NodaTime.TimeZones; 

/// <summary> 
/// Functions to Convert To and From UTC 
/// </summary> 
public class Utils 
{ 
    /// <summary> 
    /// The date format for display/compare 
    /// </summary> 
    public const string Format = "yyyy-MM-dd HH:mm:ss.ffffff"; 

    /// <summary> 
    /// The eastern U.S. time zone 
    /// </summary> 
    private static readonly NodaTime.DateTimeZone BclEast = NodaTime.DateTimeZoneProviders.Bcl.GetZoneOrNull("Eastern Standard Time"); 


    private static readonly TimeZoneInfo EasternTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time"); 

    private static readonly NodaTime.DateTimeZone TzEast = NodaTime.DateTimeZoneProviders.Tzdb.GetZoneOrNull("America/New_York"); 

    private static readonly ZoneLocalMappingResolver CustomResolver = Resolvers.CreateMappingResolver(Resolvers.ReturnLater, Resolvers.ReturnStartOfIntervalAfter); 

    public static DateTime GetUtc(DateTime ts) 
    { 
     return TimeZoneInfo.ConvertTimeToUtc(EasternTimeZone.IsInvalidTime(ts) ? ts.AddHours(1.0) : ts, EasternTimeZone); 
    } 

    public static DateTime GetUtcTz(DateTime ts) 
    { 
     var local = LocalDateTime.FromDateTime(ts); 
     var zdt = TzEast.ResolveLocal(local, CustomResolver); 
     return zdt.ToDateTimeUtc();    
    } 

    public static DateTime GetUtcBcl(DateTime ts) 
    { 
     var local = LocalDateTime.FromDateTime(ts); 
     var zdt = BclEast.ResolveLocal(local, CustomResolver); 
     return zdt.ToDateTimeUtc(); 
    } 
} 

risposta

7

NodaTime non sembra avere un equivalente di TimeZoneInfo.IsInvalidTime.

Bene, invece di chiedere una sola domanda - e poi dover chiedere a quelli successivi - si usa DateTimeZone.MapLocal. Questo ti dà tutto ciò che potresti sapere su una mappatura da locale a UTC: se è ambigua, ambigua o non valida.

In alternativa, utilizzare ResolveLocal ma con il proprio delegato SkippedTimeResolver personalizzato.

Ad esempio, rendendo questo cambiamento rende il vostro lavoro codice per me:

private static readonly ZoneLocalMappingResolver CustomResolver = 
    Resolvers.CreateMappingResolver(Resolvers.ReturnLater, AddGap); 

// SkippedTimeResolver which adds the length of the gap to the 
// local date and time. 
private static ZonedDateTime AddGap(LocalDateTime localDateTime, 
            DateTimeZone zone, 
            ZoneInterval intervalBefore, 
            ZoneInterval intervalAfter)   
{ 
    long afterMillis = intervalAfter.WallOffset.Milliseconds; 
    long beforeMillis = intervalBefore.WallOffset.Milliseconds; 
    Period gap = Period.FromMilliseconds(afterMillis - beforeMillis); 
    return zone.AtStrictly(localDateTime + gap); 
} 

(ci sono altri modi equivalenti di farlo, naturalmente.)

io personalmente suggerisco cercando di evitare conversione da e per DateTime a meno che non sia davvero necessario - farei il più possibile in Noda Time.

+0

Ho aggiornato i collegamenti di riferimento in questa risposta per portare alla documentazione dell'API più recente ospitata su nodatime.org anziché sul vecchio sito mancante di codice google. – clarkitect