Convertire l'ora UTC / GMT in ora locale


301

Stiamo sviluppando un'applicazione C # per un client di servizi Web. Questo funzionerà su PC Windows XP.

Uno dei campi restituiti dal servizio Web è un campo DateTime. Il server restituisce un campo in formato GMT, ovvero con una "Z" alla fine.

Tuttavia, abbiamo scoperto che .NET sembra fare una sorta di conversione implicita e il tempo era sempre di 12 ore.

Il seguente esempio di codice risolve questo in una certa misura in quanto la differenza di 12 ore è andata ma non tiene conto dell'ora legale della Nuova Zelanda.

CultureInfo ci = new CultureInfo("en-NZ");
string date = "Web service date".ToString("R", ci);
DateTime convertedDate = DateTime.Parse(date);            

Secondo questa data sito :

Offset UTC / GMT

Fuso orario standard: UTC / GMT +12 ore
Ora legale: +1 ora
Offset fuso orario corrente: UTC / GMT +13 ore

Come ci adattiamo all'ora extra? Questo può essere fatto a livello di codice o è una sorta di impostazione sul PC?


2
l' Zora fa riferimento a UTC, non GMT. I due possono differire fino a 0,9 secondi.
mc0e

Risposte:


374

Per stringhe come 2012-09-19 01:27:30.000, DateTime.Parsenon è possibile stabilire da quale fuso orario siano la data e l'ora.

DateTimeha una proprietà Kind , che può avere una delle tre opzioni di fuso orario:

  • imprecisato
  • Locale
  • UTC

NOTA Se si desidera rappresentare una data / ora diversa da UTC o dal fuso orario locale, è necessario utilizzare DateTimeOffset.


Quindi per il codice nella tua domanda:

DateTime convertedDate = DateTime.Parse(dateStr);

var kind = convertedDate.Kind; // will equal DateTimeKind.Unspecified

Dici di sapere che tipo è, quindi dillo.

DateTime convertedDate = DateTime.SpecifyKind(
    DateTime.Parse(dateStr),
    DateTimeKind.Utc);

var kind = convertedDate.Kind; // will equal DateTimeKind.Utc

Ora, una volta che il sistema è a conoscenza dell'ora UTC, puoi semplicemente chiamare ToLocalTime:

DateTime dt = convertedDate.ToLocalTime();

Questo ti darà il risultato desiderato.


19
solo un altro modo per specificare il tipo:DateTime convertedTime = new DateTime(DateTime.Parse(dateStr).Ticks), DateTimeKind.Utc);
Brad

non è ToLocalTime ()? @Brad - i tuoi genitori non corrispondono.
TrueWill

2
Questa soluzione tiene conto dell'ora legale? Quando lo provo, vado via un'ora.
Bob Horn,

7
La fase di modifica del Kindda DateTimeda Unspecifieda UTCnon è necessaria. Unspecifiedsi presume che sia UTCai fini di ToLocalTime: msdn.microsoft.com/en-us/library/…
CJ7

16
@ CJ7: Sì, ma essere espliciti è particolarmente utile per altri sviluppatori che potrebbero dover mantenere il codice.
Ryan,

121

Esaminerei l'utilizzo della classe System.TimeZoneInfo se ti trovi in ​​.NET 3.5. Vedi http://msdn.microsoft.com/en-us/library/system.timezoneinfo.aspx . Ciò dovrebbe tener conto correttamente delle modifiche dell'ora legale.

// Coordinated Universal Time string from 
// DateTime.Now.ToUniversalTime().ToString("u");
string date = "2009-02-25 16:13:00Z"; 
// Local .NET timeZone.
DateTime localDateTime = DateTime.Parse(date); 
DateTime utcDateTime = localDateTime.ToUniversalTime();

// ID from: 
// "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Time Zone"
// See http://msdn.microsoft.com/en-us/library/system.timezoneinfo.id.aspx
string nzTimeZoneKey = "New Zealand Standard Time";
TimeZoneInfo nzTimeZone = TimeZoneInfo.FindSystemTimeZoneById(nzTimeZoneKey);
DateTime nzDateTime = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, nzTimeZone);

Se lavori nel tuo fuso orario (en-NZ in questo caso), allora non devi occuparti di TimeZoneInfo. È solo complessità inutile. Vedi la mia risposta per maggiori dettagli.
Drew Noakes,

11
E se qualcuno ha bisogno, ecco l'elenco dei fusi orari che ho trovato per TimeZoneInfo.FindSystemTimeZoneById - codeproject.com/Messages/3867850/…
nikib3ro

Brillante! Grazie per questo post Dan. Ho cercato questa soluzione per 3 giorni.
Kevin Moore,

58
TimeZone.CurrentTimeZone.ToLocalTime(date);

8
Funziona solo se il sistema sa che la data di conversione è in UTC. Si prega di vedere la mia risposta.
Drew Noakes,

1
Ma UTC è l'impostazione predefinita, no? Quindi funziona per "non specificato" come nella risposta di CJ7.
NickG

25

DateTimeoggetti hanno la Kinddi Unspecifieddefault, che ai fini di ToLocalTimesi presume essere UTC.

Per ottenere l'ora locale di un Unspecified DateTimeoggetto, devi solo fare questo:

convertedDate.ToLocalTime();

La fase di modifica del Kindda DateTimeda Unspecifieda UTCnon è necessaria. Unspecifiedsi presume che sia UTCai fini di ToLocalTime: http://msdn.microsoft.com/en-us/library/system.datetime.tolocaltime.aspx


6
E viceversa: convertedDate.FromLocalTime();convertirà in UTC.
R. Schreurs,

16

So che questa è una domanda più vecchia, ma mi sono imbattuto in una situazione simile e volevo condividere ciò che avevo trovato per i futuri ricercatori, incluso eventualmente me stesso :).

DateTime.Parse()può essere difficile - vedi qui per esempio.

Se DateTimeproviene da un servizio Web o da qualche altra fonte con un formato noto, potresti prendere in considerazione qualcosa del genere

DateTime.ParseExact(dateString, 
                   "MM/dd/yyyy HH:mm:ss", 
                   CultureInfo.InvariantCulture, 
                   DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal)

o, ancora meglio,

DateTime.TryParseExact(...)

Il AssumeUniversalflag indica al parser che la data / ora è già UTC; la combinazione di AssumeUniversale gli AdjustToUniversaldice di non convertire il risultato in ora "locale", che proverà a fare di default. (Provo personalmente a trattare esclusivamente con UTC nei livelli business / application / service comunque. Ma bypassare la conversione all'ora locale accelera anche le cose - del 50% o più nei miei test, vedi sotto.)

Ecco cosa stavamo facendo prima:

DateTime.Parse(dateString, new CultureInfo("en-US"))

Abbiamo profilato l'app e abbiamo scoperto che DateTime.Parse rappresentava una percentuale significativa dell'utilizzo della CPU. (Per inciso, il CultureInfocostruttore non ha contribuito in modo significativo all'utilizzo della CPU.)

Quindi ho impostato un'app console per analizzare una stringa data / ora 10000 volte in vari modi. Linea di fondo:
Parse()10 sec
ParseExact()(conversione in locale) 20-45 ms
ParseExact()(non conversione in locale) 10-15 ms
... e sì, i risultati per Parse()sono in secondi , mentre gli altri sono in millisecondi .


14

Vorrei solo aggiungere una nota generale di cautela.

Se tutto ciò che stai facendo è ottenere l'ora corrente dall'orologio interno del computer per mettere una data / ora sul display o un rapporto, allora va tutto bene. Ma se stai salvando le informazioni sulla data / ora per riferimento futuro o stai calcolando la data / ora, fai attenzione!

Supponiamo che tu stabilisca che una nave da crociera è arrivata a Honolulu il 20 dicembre 2007 alle 15:00 UTC. E vuoi sapere che ora locale era.
1. Probabilmente ci sono almeno tre "locali" coinvolti. Locale può significare Honolulu, oppure può significare dove si trova il tuo computer, oppure può significare la posizione in cui si trova il tuo cliente.
2. Se usi le funzioni integrate per fare la conversione, probabilmente sarà sbagliato. Questo perché l'ora legale è (probabilmente) attualmente in vigore sul tuo computer, ma NON era in vigore a dicembre. Ma Windows non lo sa ... tutto ciò che ha è un flag per determinare se l'ora legale è attualmente in vigore. E se è attualmente in vigore, allora aggiungerà felicemente un'ora anche a una data a dicembre.
3.L'ora legale è implementata diversamente (o per niente) in varie suddivisioni politiche. Non pensare che solo perché il tuo Paese cambia in una data specifica, anche altri Paesi lo faranno.


6
In realtà, il n. 2 non è del tutto corretto. Esistono infatti regole sull'ora legale in ogni fuso orario che il tuo computer saprà se le informazioni sono state installate (e aggiornate). Per molte zone, tali regole sono fisse. Altri implementano "DST dinamico". Il Brasile è la mia pipì per questo. In sintesi, tuttavia, il tuo coputer può capire se l'ora locale sarà l'ora legale in dicembre, supponendo che da quel momento in poi non vengano introdotte modifiche alla legge.
Roger Willcocks,

Anche se non vivi in ​​Brasile, l'ora legale è "dinamica" in quanto i politici possono cambiarla in qualsiasi momento (come è stato fatto qualche anno fa negli Stati Uniti). Poiché la maggior parte dei software è stata scritta pensando all'uso futuro, è importante rendersi conto che NON esiste un modo pratico, prevedibile o persino teorico di sapere quali saranno le regole dell'ora legale. Puoi avvicinarti, ma risparmia un po 'di frustrazione rinunciando alla perfezione.
DaveWalley,


5

Non dimenticare se hai già un oggetto DateTime e non sei sicuro che sia UTC o Local, è abbastanza facile usare direttamente i metodi sull'oggetto:

DateTime convertedDate = DateTime.Parse(date);
DateTime localDate = convertedDate.ToLocalTime();

Come ci adattiamo all'ora extra?

Se non specificato .net utilizzerà le impostazioni del PC locale. Avrei letto: http://msdn.microsoft.com/en-us/library/system.globalization.daylighttime.aspx

A quanto pare il codice potrebbe assomigliare a:

DaylightTime daylight = TimeZone.CurrentTimeZone.GetDaylightChanges( year );

E come detto sopra ricontrolla quale fuso orario è impostato sul tuo server. Ci sono articoli in rete su come influenzare in modo sicuro i cambiamenti in IIS.


Il sistema gestirà questa complessità per te, a condizione che tu dica al sistema di che tipo è la tua data (locale / utc / non specificato).
Drew Noakes,

2

In risposta al suggerimento di Dana:

L'esempio di codice ora appare come:

string date = "Web service date"..ToString("R", ci);
DateTime convertedDate = DateTime.Parse(date);            
DateTime dt = TimeZone.CurrentTimeZone.ToLocalTime(convertedDate);

La data originale era il 20/08/08; il tipo era UTC.

Sia "ConvertDate" che "dt" sono uguali:

21/08/08 10:00:26; il tipo era locale


Si prega di consultare la mia risposta per una spiegazione di questo.
Drew Noakes,

1

Ho avuto il problema che si trovava in un set di dati che veniva spinto attraverso il filo (servizio web al client) che sarebbe cambiato automaticamente perché il campo DateType di DataColumn era impostato su locale. Assicurati di controllare qual è il DateType se stai inviando DataSet.

Se non vuoi che cambi, impostalo su Non specificato


1

Mi sono imbattuto in questa domanda in quanto avevo un problema con le date UTC che torni attraverso l'API di Twitter (campo Created_at su uno stato); Devo convertirli in DateTime. Nessuna delle risposte / esempi di codice nelle risposte in questa pagina sono state sufficienti per impedirmi di ottenere un errore "La stringa non è stata riconosciuta come un valido DateTime" (ma è il più vicino che ho avuto per trovare la risposta corretta su SO)

Pubblicare questo link qui nel caso questo aiuti qualcun altro - la risposta di cui avevo bisogno è stata trovata in questo post del blog: http://www.wduffy.co.uk/blog/parsing-dates-when-aspnets-datetimeparse-doesnt-work/ - utilizza sostanzialmente DateTime.ParseExact con una stringa di formato anziché DateTime.Parse

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.