2013-03-04 13 views
6

Was ich erreichen möchte, ist eine DateTime (analysiert aus einer Zeichenfolge, die in EST/EDT angenommen wird) in UTC zu konvertieren. Ich verwende NodaTime, weil ich Olson Zeitzonen benutzen muss.Verwenden von NodaTime, um ungültige (übersprungene) Datetime-Werte in UTC zu konvertieren

Das Konvertieren eines ungültigen (übersprungenen) DateTime in UTC mit NodaTime's ZoneLocalMappingResolver konvertiert keine Minuten- und Sekundeneingabe, da ich CustomResolver so konfiguriert habe, dass der Beginn des Intervalls nach der Lücke zurückgegeben wird. NodaTime scheint kein Äquivalent von TimeZoneInfo.IsInvalidTime zu haben.

Wie verwende ich NodaTime, um übersprungene Datetime-Werte in UTC zu konvertieren und das Ergebnis der GetUtc() - Methode in der Utils-Klasse unten zu vergleichen? (Utils.GetUtc Methode verwendet System.TimeZoneInfo nicht NodaTime)

Dies ist der Testfall:

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

Dies ist, was ich erhalte:

Assert.AreEqual fehlgeschlagen. Erwartet: < 2013-03-10 07: 15: 45.000000>. Aktuell: < 2013-03-10 07: 00: 00.000000>.

Hier ist die Utils-Klasse (auch auf 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(); 
    } 
} 

Antwort

7

NodaTime scheint nicht ein Äquivalent von TimeZoneInfo.IsInvalidTime zu haben.

Nun, anstatt nur diese eine Frage zu stellen - und dann nachfragen zu müssen - verwenden Sie DateTimeZone.MapLocal. Damit erhalten Sie alles, was Sie über ein UTC-Mapping wissen können: ob es eindeutig, zweideutig oder ungültig ist.

Alternativ verwenden Sie ResolveLocal aber mit Ihrem eigenen benutzerdefinierten SkippedTimeResolver Delegaten.

Zum Beispiel dieser Änderung macht den Code für mich arbeiten:

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

(Es gibt andere gleichwertige Möglichkeiten, es zu tun, natürlich.)

ich persönlich versuchen würde vorschlagen, zu vermeiden Konvertieren zu und von DateTime, wenn Sie wirklich müssen - ich würde so viel wie möglich in Noda Time tun.

+0

Ich habe die Referenzlinks in dieser Antwort aktualisiert, um zu Ihrer neueren API-Dokumentation zu gelangen, die auf nodatime.org gehostet wird, anstelle der alten, fehlenden Google-Code-Site. – clarkitect