2012-04-05 4 views
43

Voglio analizzare una stringa che rappresenta un DateTime in formato UTC.DateTime.Parse ("2012-09-30T23: 00: 00.0000000Z") converte sempre in DateTimeKind.Local

La mia rappresentazione di stringa include la specifica del tempo Zulu che dovrebbe indicare che la stringa rappresenta un tempo UTC.

var myDate = DateTime.Parse("2012-09-30T23:00:00.0000000Z");  

Da quanto sopra mi aspetterei di essere myDate.Kind DateTimeKind.Utc, invece è DatetimeKind.Local.

Cosa sto facendo male e come analizzare una stringa che rappresenta un tempo UTC?

Grazie mille!

risposta

64

userei Noda Time personalmente, ma se non si può fare che ...

usare sia DateTime.ParseExact specificando il formato esatto che ci si aspetta, e comprendono DateTimeStyles.AssumeUniversal e DateTimeStyles.AdjustToUniversal nel codice parse:

using System; 
using System.Globalization; 

class Test 
{ 
    static void Main()   
    { 
     var date = DateTime.ParseExact("2012-09-30T23:00:00.0000000Z", 
             "yyyy-MM-dd'T'HH:mm:ss.fffffff'Z'", 
             CultureInfo.InvariantCulture, 
             DateTimeStyles.AssumeUniversal | 
             DateTimeStyles.AdjustToUniversal); 
     Console.WriteLine(date); 
     Console.WriteLine(date.Kind); 
    } 
} 

(abbastanza perché sarebbe un di norma senza AdjustToUniversal è oltre me, ma non importa ...)

EDIT: Solo per espandere le mie obiezioni al suggerimento di mattytommo, ho mirato a dimostrare che avrebbe perso le informazioni. Ho fallito finora - ma in un modo molto particolare. Date un'occhiata a questo - in esecuzione nel fuso orario Europe/London, dove gli orologi tornano il 28 ottobre nel 2012, alle 02:00 ora locale (UTC 01:00):

DateTime local1 = DateTime.Parse("2012-10-28T00:30:00.0000000Z"); 
DateTime local2 = DateTime.Parse("2012-10-28T01:30:00.0000000Z"); 
Console.WriteLine(local1 == local2); // True 

DateTime utc1 = TimeZoneInfo.ConvertTimeToUtc(local1); 
DateTime utc2 = TimeZoneInfo.ConvertTimeToUtc(local2); 
Console.WriteLine(utc1 == utc2); // False. Hmm. 

Sembra che ci sia un "con o senza il flag "DST" memorizzato da qualche parte, ma sarò bloccato se riesco a capire dove. La documentazione per TimeZoneInfo.ConvertTimeToUtc stato

Se dateTime corrisponde ad un tempo ambiguo, questo metodo presuppone che è il tempo standard del tempo sorgente orario.

Ciò non sembrano sia il caso qui durante la conversione local2 ...

EDIT: Okay, diventa ancora più strano - dipende quale versione del quadro che si sta utilizzando. Considerate questo programma:

using System; 
using System.Globalization; 

class Test 
{ 
    static void Main()   
    { 
     DateTime local1 = DateTime.Parse("2012-10-28T00:30:00.0000000Z"); 
     DateTime local2 = DateTime.Parse("2012-10-28T01:30:00.0000000Z"); 

     DateTime utc1 = TimeZoneInfo.ConvertTimeToUtc(local1); 
     DateTime utc2 = TimeZoneInfo.ConvertTimeToUtc(local2); 
     Console.WriteLine(utc1); 
     Console.WriteLine(utc2); 

     DateTime utc3 = local1.ToUniversalTime(); 
     DateTime utc4 = local2.ToUniversalTime(); 
     Console.WriteLine(utc3); 
     Console.WriteLine(utc4); 
    } 
} 

Quindi questo richiede due diversi valori UTC, li analizza con DateTime.Parse, poi li converte di nuovo a UTC in due modi diversi.

Risultati sotto .NET 3.5:

28/10/2012 01:30:00 // Look - we've lost information 
28/10/2012 01:30:00 
28/10/2012 00:30:00 // But ToUniversalTime() seems okay... 
28/10/2012 01:30:00 

risultati sotto .NET 4.5 beta:

28/10/2012 00:30:00 // It's okay! 
28/10/2012 01:30:00 
28/10/2012 00:30:00 
28/10/2012 01:30:00 
+1

Un valore DateTime memorizza il suo tipo nei suoi due bit più significativi: Non specificato (00), Utc (01), Locale (10) e LocalAmbiguousDst (11). Tuttavia, LocalAmbiguousDst è esposto pubblicamente come Locale. –

+1

@ MichaelLiu: Giusto. Quindi, finché non lo converti in universale, non puoi dire la differenza. Che bello. Ick. –

+0

@JonSkeet Significa che il mio codice era corretto (ad eccezione di ParseExact) in quanto deve essere prima convertito in universale? – mattytommo

4

utilizzare la classe TimeZoneInfo usando la seguente:

var myDate = TimeZoneInfo.ConvertTimeToUtc(DateTime.Parse("2012-09-30T23:00:00.0000000Z")); 
+4

Cattiva idea IMO. Se il tempo analizzato finisce per essere * ambiguo * nel fuso orario locale, hai praticamente perso le informazioni. Evita la conversione non necessaria, dicendo al parser di analizzarlo come un tempo universale per iniziare. –

+0

@JonSkeet Che dire se ParseExact è stato utilizzato al posto di analisi, ma continuando a utilizzare la classe TimeZoneInfo, considereresti comunque una cattiva idea? – mattytommo

+0

Sono d'accordo con Jon, cattiva idea. –

23

Come al solito, la risposta di Jon è molto completa. Detto questo, nessuno ha ancora menzionato lo DateTimeStyles.RoundtripKind. Se si desidera convertire un DateTime in una stringa e tornare allo stesso DateTime (inclusa la conservazione dell'impostazione DateTime.Kind), utilizzare il flag DateTimeStyles.RoundtripKind.

Come ha detto Jon, la cosa corretta da fare è utilizzare il formattatore "O" quando si converte un oggetto DateTime in una stringa. Ciò conserva sia la precisione che le informazioni sul fuso orario. Ancora una volta, come ha detto Jon, utilizzare DateTime.ParseExact durante la riconversione. Ma se si utilizza DateTimeStyles.RoundtripKind, si ottiene sempre indietro ciò che si mette in:

var now = DateTime.UtcNow; 
var strNow = now.ToString("O"); 
var newNow = DateTime.ParseExact(strNow, "O", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); 

Nel codice di cui sopra, è un newNow tempo stesso esatto come now, compreso il fatto che si tratta di UTC. Se si esegue lo stesso codice eccetto ilsostitutivo per DateTime.UtcNow, si otterrà una copia esatta di now indietro come newNow, ma questa volta come ora locale.

Per i miei scopi, questa era la cosa giusta poiché volevo essere sicuro che tutto ciò che era passato e convertito tornasse alla stessa identica cosa.

+0

Grazie mille. Stavo cercando questa bandiera! Il consiglio di Jon convertirà tutte le date in UTC indipendentemente dal fuso orario originale nella stringa. – irriss

2

È possibile utilizzare il seguente formato per il metodo di parser: yyyy-MM-ddTHH:mm:ss.ffffffK

Questo deve elaborare correttamente le informazioni sul fuso orario alla fine (starting from .NET 2.0).

RE: ISO 8601

+0

Questa è la "K" che stavo cercando, grazie! –

2

sono imbattuto in un problema simile prima e diverse ore (e capelli tirati) in seguito finito per usare DateTime.SpecifyKind:

DateTime.SpecifyKind(inputDate, DateTimeKind.Utc); 

Credo che qualcuno anche eluso a questo in un commento di cui sopra come bene.

Problemi correlati