2016-03-28 9 views
7

Sto provando a creare un piccolo metodo che converte l'ora da un fuso orario a un altro. Ho pensato che sarebbe stato abbastanza semplice, ma quando lo distribuisco ottengo questo errore The UTC Offset of the local dateTime parameter does not match the offset argument. La mia ipotesi è che è perché il server non si trova nello stesso fuso orario dell'utente che non è utile poiché questo sarebbe utilizzato da tutto il mondo.DateTimeOffset Errore: l'offset UTC di dateTime locali non corrisponde all'argomento di offset

public object ConvertDate(DateTime inputTime, string fromOffset, string toZone) 
{ 
    var fromTimeOffset = new TimeSpan(0, - int.Parse(fromOffset), 0); 
    var to = TimeZoneInfo.FindSystemTimeZoneById(toZone); 
    var offset = new DateTimeOffset(inputTime, fromTimeOffset); 
    var destination = TimeZoneInfo.ConvertTime(offset, to); 
    return destination.DateTime; 
} 

Dove fromOffset è un numero, convertito al periodo dal fuso orario gli utenti e toZone è il nome della zona stiamo conversione in. L'errore si verifica su questa riga var offset = new DateTimeOffset(inputTime, fromTimeOffset);

Qualche idea su come ottenere questo funzionamento?

+0

quali sono i valori di esempio per 'inputTime, fromTimeOffset' che si sta passando –

+0

un esempio potrebbe essere:' inputTime = 28/03/2016 06:09:49 PM' e 'fromTimeOffset = 13hrs' – Toxicable

+0

Se ti sei mai ritrovato a dire "Ho pensato che sarebbe stato abbastanza semplice ..." per quanto riguarda il C# "DateTime", vale la pena leggere il celebre romanzo di Jon Skeet (in) "Cosa c'è che non va in DateTime?" post sulla nascita della libreria NodaTime: http://blog.nodatime.org/2011/08/what-wrong-with-datetime-anyway.html – rob3c

risposta

14

Vedere la documentation del motivo per cui viene generata l'eccezione:

ArgumentException: dateTime.Kind equals Local and offset does not equal the offset of the system's local time zone.

Il DateTime argomento che si riceve ha la sua proprietà Kind impostata Local. Il modo più semplice per risolvere questo problema è impostare Kind su Undefined.

public object ConvertDate(DateTime inputTime, string fromOffset, string toZone) 
{ 
    // Ensure that the given date and time is not a specific kind. 
    inputTime = DateTime.SpecifyKind(inputTime, DateTimeKind.Unspecified); 

    var fromTimeOffset = new TimeSpan(0, - int.Parse(fromOffset), 0); 
    var to = TimeZoneInfo.FindSystemTimeZoneById(toZone); 
    var offset = new DateTimeOffset(inputTime, fromTimeOffset); 
    var destination = TimeZoneInfo.ConvertTime(offset, to); 
    return destination.DateTime; 
} 
+0

Ah che l'ho risolto grazie, ho letto la documentazione ma l'ho trovata un po 'difficile da interpretare tbh – Toxicable

+0

Questo è così ridicolo, rende un incubo lavorare con DateTimeOffsets. Se qualcuno * esplicitamente * imposta l'offset su un nuovo DTO, potrebbe aver realizzato che volevamo che avesse un offset e quindi consentirlo? E potrebbero aver capito che nella maggior parte dei casi, l'impostazione del DateTime.Kind originale non era nelle nostre mani? E peggio, che la costante assunzione di usare l'ora locale (mi dispiace, che è ORRIBILE per i server) ci mette in questa situazione in primo luogo? Questa è una decisione quadro così frustrante che causa molto dolore. –

1

Ecco un metodo di estensione che utilizzo per aggirare questa decisione quadro incredibilmente frustrante. Vedere le note, il modo migliore e più efficace per gestire questo è utilizzare il costruttore tick di DateTimeOffset, invece di dover allocare un altro intermedio DateTime solo per cambiare la sua proprietà Kind.

/// <summary> 
    /// Converts a DateTime to a DateTimeOffset, without risking any onerous exceptions 
    /// the framework quite unfortunately throws within the DateTimeOffset constructor, 
    /// such as they do when the source DateTime's Kind is not set to UTC. The best and 
    /// most performant way around this, which we do herein, is to simply construct the 
    /// new DateTimeOffset with the overload that excepts Ticks. Also, we will simply 
    /// return <see cref="DateTimeOffset.MinValue"/> if the source DateTime was 
    /// <see cref="DateTime.MinValue"/>. 
    /// </summary> 
    /// <param name="dt">Source DateTime.</param> 
    /// <param name="offset">Offset</param> 
    public static DateTimeOffset ToDateTimeOffset(this DateTime dt, TimeSpan offset) 
    { 
     // adding negative offset to a min-datetime will throw, this is a 
     // sufficient catch. Note however that a DateTime of just a few hours can still throw 
     if (dt == DateTime.MinValue) 
      return DateTimeOffset.MinValue; 

     return new DateTimeOffset(dt.Ticks, offset); 
    } 

    public static DateTimeOffset ToDateTimeOffset(this DateTime dt, double offsetInHours = 0) 
     => ToDateTimeOffset(dt, offsetInHours == 0 ? TimeSpan.Zero : TimeSpan.FromHours(offsetInHours));