DateTime vs DateTimeOffset


743

Attualmente, abbiamo un modo standard di gestire .NET DateTimein modo consapevole TimeZone: ogni volta che produciamo un DateTimelo facciamo in UTC (es. Usando DateTime.UtcNow), e ogni volta che ne visualizziamo uno, riconvertiamo da UTC all'ora locale dell'utente .

Funziona bene, ma ho letto DateTimeOffsete come cattura l'ora locale e l'ora UTC nell'oggetto stesso. Quindi la domanda è: quali sarebbero i vantaggi dell'utilizzo DateTimeOffsetrispetto a quello che abbiamo già fatto?


3
Di seguito sono riportate alcune ottime risposte. Ma mi chiedo ancora cosa, se non altro, potrebbe averti convinto a iniziare a usare DateTimeOffset.
HappyNomad,


Quando si tratta di archiviazione, anche stackoverflow.com/questions/4715620/… è interessante.
Dejan,

Risposte:


1169

DateTimeOffsetè una rappresentazione del tempo istantaneo (noto anche come tempo assoluto ). Con ciò intendo un momento nel tempo che è universale per tutti (senza tenere conto dei secondi bisestili o degli effetti relativistici della dilatazione del tempo ). Un altro modo per rappresentare il tempo istantaneo è con un DateTimedove si .Kindtrova DateTimeKind.Utc.

Questo è distinto dal tempo di calendario (noto anche come tempo civile ), che è una posizione sul calendario di qualcuno e ci sono molti calendari diversi in tutto il mondo. Chiamiamo questi fusi orari dei calendari . L'ora del calendario è rappresentata da un DateTimedove si .Kindtrova DateTimeKind.Unspecifiedo DateTimeKind.Local. Ed .Localè significativo solo in scenari in cui hai una comprensione implicita di dove è posizionato il computer che sta usando il risultato. (Ad esempio, una workstation dell'utente)

Quindi, allora, DateTimeOffsetinvece di un UTC DateTime? Si tratta di prospettiva. Usiamo un'analogia: faremo finta di essere fotografi.

Immagina di essere in piedi su una sequenza temporale del calendario, puntando una telecamera verso una persona sulla sequenza temporale istantanea disposta di fronte a te. Allinea la tua fotocamera in base alle regole del tuo fuso orario, che cambiano periodicamente a causa dell'ora legale o di altre modifiche alla definizione legale del tuo fuso orario. (Non hai una mano ferma, quindi la tua fotocamera è traballante.)

La persona in piedi nella foto vedrebbe l'angolazione da cui proveniva la tua fotocamera. Se gli altri stessero fotografando, potrebbero essere da angolazioni diverse. Questo è ciò che rappresenta la Offsetparte del DateTimeOffset.

Quindi, se etichetti la tua fotocamera "Eastern Time", a volte stai puntando da -5 e a volte stai puntando da -4. Esistono telecamere in tutto il mondo, tutte etichettate in modo diverso e tutte puntate sulla stessa timeline istantanea da angolazioni diverse. Alcuni di essi si trovano uno accanto all'altro (o uno sopra l'altro), quindi è sufficiente conoscere l'offset per determinare a quale fuso orario è correlato il tempo.

E che dire di UTC? Bene, è l'unica fotocamera là fuori che è garantita per avere una mano ferma. È su un treppiede, saldamente ancorato al terreno. Non sta andando da nessuna parte. Chiamiamo il suo angolo di prospettiva l'offset zero.

Visualizzazione tempo istantaneo vs tempo calendario

Quindi, cosa ci dice questa analogia? Fornisce alcune linee guida intuitive

  • Se stai rappresentando il tempo relativo a un luogo in particolare, rappresentalo nel tempo di calendario con a DateTime. Basta essere sicuri di non confondere mai un calendario con un altro. Unspecifieddovrebbe essere il tuo presupposto. Localè utile solo venire DateTime.Now. Ad esempio, potrei ottenerlo DateTime.Nowe salvarlo in un database, ma quando lo recupero, devo presumere che lo sia Unspecified. Non posso fare affidamento sul fatto che il mio calendario locale è lo stesso calendario da cui è stato originariamente preso.

  • Se devi essere sempre certo del momento, assicurati di rappresentare il tempo istantaneo. Utilizzare DateTimeOffsetper applicarlo o utilizzare UTC DateTimeper convenzione.

  • Se devi tenere traccia di un momento di tempo istantaneo, ma vuoi anche sapere "A che ora ha pensato l'utente che fosse sul suo calendario locale?" - allora devi usare a DateTimeOffset. Questo è molto importante per i sistemi di cronometraggio, ad esempio, sia per problemi tecnici che legali.

  • Se hai mai bisogno di modificarne uno precedentemente registrato DateTimeOffset, non hai abbastanza informazioni nell'offset da solo per assicurarti che il nuovo offset sia ancora rilevante per l'utente. Devi anche memorizzare un identificatore del fuso orario (pensa: ho bisogno del nome di quella fotocamera per poter scattare una nuova foto anche se la posizione è cambiata).

    Va anche sottolineato che Noda Time ha una rappresentazione richiesta ZonedDateTimeper questo, mentre la libreria di classi base .Net non ha nulla di simile. Dovresti archiviare sia DateTimeOffsetun TimeZoneInfo.Idvalore che un valore.

  • Occasionalmente, vorrai rappresentare un orario di calendario locale per "chiunque lo guardi". Ad esempio, quando si definisce cosa significa oggi . Oggi è sempre da mezzanotte a mezzanotte, ma rappresentano un numero pressoché infinito di intervalli sovrapposti sulla linea temporale istantanea. (In pratica abbiamo un numero finito di fusi orari, ma puoi esprimere gli offset fino al segno di spunta) Quindi in queste situazioni, assicurati di capire come limitare il "chi sta chiedendo?" fare una domanda in un unico fuso orario o occuparsi di tradurli di nuovo in tempo istantaneo come appropriato.

Ecco alcuni altri piccoli dettagli su DateTimeOffsetquesto backup di questa analogia e alcuni suggerimenti per mantenerlo chiaro:

  • Se si confrontano due DateTimeOffsetvalori, vengono prima normalizzati a zero offset prima del confronto. In altre parole, 2012-01-01T00:00:00+00:00e si 2012-01-01T02:00:00+02:00riferiscono allo stesso momento istantaneo e sono quindi equivalenti.

  • Se si sta eseguendo test di unità e si deve essere certi dell'offset, verificare sia il DateTimeOffsetvalore che la .Offsetproprietà separatamente.

  • Esiste una conversione implicita unidirezionale incorporata nel framework .Net che consente di passare a DateTimequalsiasi DateTimeOffsetparametro o variabile. Nel fare ciò, le .Kindcose . Se si passa un tipo UTC, verrà eseguito con un offset zero, ma se si passa uno .Localo o .Unspecified, assumerà che sia locale . Fondamentalmente il framework sta dicendo: "Beh, mi hai chiesto di convertire il tempo del calendario in tempo istantaneo, ma non ho idea da dove provenga, quindi userò solo il calendario locale". Questo è un grosso problema se si carica un non specificato DateTimesu un computer con un fuso orario diverso. (IMHO - che dovrebbe generare un'eccezione - ma non lo fa.)

Spina senza vergogna:

Molte persone hanno condiviso con me che trovano questa analogia estremamente preziosa, quindi l'ho inclusa nel mio corso Pluralsight, Date and Time Fundamentals . Troverai una procedura dettagliata dell'analogia della telecamera nel secondo modulo, "Context Matters", nella clip intitolata "Tempo calendario vs. tempo istantaneo".


4
@ZackJannsen Se hai un DateTimeOffsetin C #, allora dovresti insistere con un DATETIMEOFFSETin SQL Server. DATETIME2o solo DATETIME(a seconda dell'intervallo richiesto) vanno bene per i DateTimevalori regolari . Sì: è possibile risolvere l'ora locale da qualsiasi abbinamento di fuso orario + dto o utc. La differenza è: vuoi sempre calcolare le regole ad ogni risoluzione o vuoi precalcolarle? In molti casi (a volte per motivi legali) un DTO è una scelta migliore.
Matt Johnson-Pint,

3
@ZackJannsen Per la seconda parte della tua domanda, consiglierei di fare il più possibile sul lato server. Javascript non è eccezionale per il calcolo del fuso orario. Se è necessario, utilizzare una di queste librerie . Ma il lato server è il migliore. Se hai altre domande più dettagliate, ti preghiamo di iniziare una nuova domanda SO per loro e risponderò se posso. Grazie.
Matt Johnson-Pint,

4
@JoaoLeme - Dipende da dove l'hai ottenuto. Hai ragione a dire che se dici DateTimeOffset.Nowsul server, otterrai effettivamente l'offset del server. Il punto è che il DateTimeOffsettipo può conservare quell'offset. Potresti farlo altrettanto facilmente sul client, inviarlo al server e quindi il tuo server saprebbe l'offset del client.
Matt Johnson-Pint,

8
Adoro davvero l'analogia della fotocamera.
Sachin Kainth,

2
Sì, è corretto. Tranne che DTO è memorizzato come coppia (ora locale, offset), non come coppia (ora utc, offset). In altre parole, l'offset da UTC si riflette già nell'ora locale. Per riconvertire in utc, invertire il segno dell'offset e applicarlo all'ora locale.
Matt Johnson-Pint,

328

Da Microsoft:

Questi usi per i valori DateTimeOffset sono molto più comuni di quelli per i valori DateTime. Di conseguenza, DateTimeOffset deve essere considerato il tipo di data e ora predefinito per lo sviluppo dell'applicazione.

fonte: "Scelta tra DateTime, DateTimeOffset, TimeSpan e TimeZoneInfo" , MSDN

Usiamo DateTimeOffsetper quasi tutto come la nostra applicazione gestisce particolari punti nel tempo (ad esempio quando un record è stato creato / aggiornato). Come nota a margine, utilizziamo anche DATETIMEOFFSETin SQL Server 2008.

Vedo DateTimecome essere utile quando si vuole affrontare solo le date, gli orari solo, o trattare con sia in senso generico. Ad esempio, se si dispone di un allarme che si vuole andare fuori ogni giorno alle 7 del mattino, è possibile memorizzare che in un DateTimeutilizzando una DateTimeKinddi Unspecifiedperché si vuole che vada via a 7am a prescindere dalla DST. Ma se si desidera rappresentare la cronologia delle occorrenze di allarmi, è consigliabile utilizzarla DateTimeOffset.

Prestare attenzione quando si utilizza un mix di DateTimeOffsete in DateTimeparticolare quando si assegna e si confronta tra i tipi. Inoltre, confronta solo DateTimeistanze uguali DateTimeKindperché DateTimeignora l'offset del fuso orario durante il confronto.


146
La risposta accettata è eccessivamente lunga e l'analogia è tesa, questa è una risposta IMO molto migliore e più concisa.
Nexus dice l'

10
Dirò solo che mi piace anche questa risposta, e votato. Sebbene nell'ultima parte - anche garantendo che Kindsiano gli stessi, il confronto potrebbe essere errato. Se entrambe le parti hanno DateTimeKind.Unspecified, non sai davvero che provengono dallo stesso fuso orario. Se entrambe le parti lo sono DateTimeKind.Local, la maggior parte dei confronti andrà bene, ma potresti comunque avere errori se una parte è ambigua nel fuso orario locale. Davvero solo i DateTimeKind.Utcconfronti sono infallibili e sì, di DateTimeOffsetsolito è preferito. (Saluti!)
Matt Johnson-Pint,

1
+1 Aggiungerei a questo: il tipo di dati che scegli dovrebbe riflettere le tue intenzioni. Non utilizzare DateTimeOffset ovunque, solo la causa. Se l'offset è importante per i tuoi calcoli e la lettura da / persistente a base di dati, usa DateTimeOffset. Se non ha importanza, usa DateTime, in modo da capire (semplicemente guardando DataType) che l'offset non dovrebbe avere rilevanza e che i tempi dovrebbero rimanere relativi alla località del server / macchina su cui è in esecuzione il tuo codice C #.
MikeTeeVee,

"Ma se si desidera rappresentare la cronologia delle occorrenze di allarmi, utilizzare DateTimeOffset." Ti andrebbe di spiegare perché pensi che sia? Potrei compilarlo leggendo tutte le informazioni in questa pagina, ma forse potresti fornirle anche in un modo facile da leggere. Questa è una risposta molto leggibile.
Barrosy,

77

DateTime è in grado di memorizzare solo due orari distinti, l'ora locale e l'ora UTC. The Kind proprietà indica quale.

DateTimeOffset si espande in questo modo essendo in grado di memorizzare orari locali da qualsiasi parte del mondo. Memorizza anche l' offset tra l'ora locale e l'ora UTC. Nota come DateTime non può farlo a meno che tu non aggiunga un membro extra alla tua classe per memorizzare quell'offset UTC. O lavora sempre e solo con UTC. Che di per sé è una buona idea tra.


33

Ci sono alcuni posti dove DateTimeOffsetha senso. Uno è quando hai a che fare con eventi ricorrenti e ora legale. Diciamo che voglio impostare un allarme per suonare ogni giorno alle 9:00. Se utilizzo la regola "Memorizza come UTC, visualizza come ora locale", la sveglia si spegnerà in modo diverso momento quando è in vigore l'ora legale.

Probabilmente ce ne sono altri, ma l'esempio sopra è in realtà uno in cui mi sono imbattuto in passato (questo era prima dell'aggiunta del DateTimeOffsetBCL - la mia soluzione al momento era quella di memorizzare esplicitamente l'ora nel fuso orario locale e salvare le informazioni sul fuso orario a fianco: fondamentalmente cosa DateTimeOffsetfa internamente).


12
DateTimeOffset non risolve il problema DST
JarrettV

2
L'uso della classe TimeZoneInfo comporta regole per l'ora legale. se si è su .net 3.5 o versioni successive, utilizzare le classi TimeZone o TimeZoneInfo per gestire le date che devono gestire l'ora legale in congiunzione con l'offset del fuso orario.
Zack Jannsen,

1
Sì, un buon esempio di eccezione (l'app per gli allarmi) ma quando l'ora è più importante della data, dovresti davvero memorizzarla nella struttura dei dati della pianificazione per l'applicazione, ad esempio tipo di occorrenza = Giornaliero e orario = 09:00. Il punto qui è che lo sviluppatore deve essere consapevole del tipo di data che stanno registrando, calcolando o presentando agli utenti. Soprattutto le app tendono ad essere più globali ora che abbiamo Internet come standard e grandi app store per cui scrivere software. Come nodo laterale vorrei anche vedere Microsoft aggiungere una struttura di data e ora separata.
Tony Wall,

3
Riassumendo i commenti di Jarrett e Zack: Sembra che DateTimeOffset da solo non gestirà il problema dell'ora legale, ma usando DateTimeOffset insieme a TimeZoneInfo lo gestirà. Questo non è diverso da DateTime dove kind è Utc. In entrambi i casi devo conoscere il fuso orario (non solo l'offset) del calendario a cui sto proiettando il momento. (Potrei memorizzarlo nel profilo di un utente o ottenerlo dal client (ad esempio Windows) se possibile). Sembra giusto?
Jeremy Cook,

"Ci sono alcuni posti in cui DateTimeOffset ha senso." --- Probabilmente, più spesso ha senso che no.
Ronnie Overby,

23

La distinzione più importante è che DateTime non memorizza le informazioni sul fuso orario, mentre DateTimeOffset lo fa.

Sebbene DateTime distingua tra UTC e Local, non vi è assolutamente alcun offset di fuso orario esplicito associato ad esso. Se si esegue qualsiasi tipo di serializzazione o conversione, verrà utilizzato il fuso orario del server. Anche se si crea manualmente un'ora locale aggiungendo minuti per compensare l'ora UTC, è comunque possibile ottenere bit nel passaggio di serializzazione, poiché (a causa della mancanza di un offset esplicito in DateTime) utilizzerà l'offset del fuso orario del server.

Ad esempio, se serializzi un valore DateTime con Kind = Local usando Json.Net e un formato data ISO, otterrai una stringa simile 2015-08-05T07:00:00-04. Nota che l'ultima parte (-04) non ha nulla a che fare con il tuo DateTime o qualsiasi offset che hai usato per calcolarlo ... è solo l'offset del fuso orario del server.

Nel frattempo, DateTimeOffset include esplicitamente l'offset. Potrebbe non includere il nome del fuso orario, ma almeno include l'offset e, se lo serializzi, otterrai l'offset incluso esplicitamente nel tuo valore anziché qualunque sia l'ora locale del server.


14
con tutte le risposte di cui sopra, mi chiedo perché nessuno si sia preso la briga di scrivere la tua singola frase che The most important distinction is that DateTime does not store time zone information, while DateTimeOffset does.
riassuma

9
DateTimeOffset NON memorizza le informazioni sul fuso orario. Documento MS intitolato "Scelta tra DateTime, DateTimeOffset, TimeSpan e TimeZoneInfo" specifica questa affermazione: "Un valore DateTimeOffset non è legato a un particolare fuso orario, ma può provenire da una qualsiasi di una varietà di fusi orari". Detto questo, DateTimeOffset IS Fuso orario AWARE, contenente l'offset da UTC, che fa la differenza ed è il motivo per cui è la classe predefinita raccomandata da MS quando si tratta di sviluppo di app che si occupa delle informazioni sulla data. Se ti interessa davvero da quale specifico fuso orario provengono i dati, devi conservarli separatamente
stonedauwg

1
Sì, ma come è stato mostrato in molti luoghi, + o - hours non dice nulla in quale fuso orario ti trovassi ed è in definitiva inutile. A seconda di ciò che devi fare, puoi anche archiviare un datetime come Kind.Unspecified e quindi memorizzare l'ID del suo fuso orario e penso che tu stia davvero meglio.
Arwin,

13

Questo pezzo di codice di Microsoft spiega tutto:

// Find difference between Date.Now and Date.UtcNow
  date1 = DateTime.Now;
  date2 = DateTime.UtcNow;
  difference = date1 - date2;
  Console.WriteLine("{0} - {1} = {2}", date1, date2, difference);

  // Find difference between Now and UtcNow using DateTimeOffset
  dateOffset1 = DateTimeOffset.Now;
  dateOffset2 = DateTimeOffset.UtcNow;
  difference = dateOffset1 - dateOffset2;
  Console.WriteLine("{0} - {1} = {2}", 
                    dateOffset1, dateOffset2, difference);
  // If run in the Pacific Standard time zone on 4/2/2007, the example
  // displays the following output to the console:
  //    4/2/2007 7:23:57 PM - 4/3/2007 2:23:57 AM = -07:00:00
  //    4/2/2007 7:23:57 PM -07:00 - 4/3/2007 2:23:57 AM +00:00 = 00:00:00

9

La maggior parte delle risposte sono buone, ma ho pensato di aggiungere altri collegamenti a MSDN per ulteriori informazioni



7

Una grande differenza è che DateTimeOffsetpuò essere utilizzata in combinazione con TimeZoneInfoper convertire in orari locali in fusi orari diversi da quello corrente.

Ciò è utile su un'applicazione server (ad esempio ASP.NET) a cui gli utenti accedono in diversi fusi orari.


3
@Bugeo Bugeo è vero, ma c'è un rischio. Puoi confrontare due DateTimes chiamando prima "ToUniversalTime" su ciascuno. Se nel confronto hai esattamente un valore, DateTimeKind = Non specificato, la tua strategia fallirà. Questo potenziale di errore è un motivo per considerare DateTimeOffset su DateTime quando sono richieste conversioni all'ora locale.
Zack Jannsen,

Come sopra, penso che in questo scenario tu stia meglio con la memorizzazione di TimeZoneId che usando DateTimeOffset, che alla fine non significa nulla.
Arwin,

2

L'unico lato negativo di DateTimeOffset che vedo è che Microsoft "ha dimenticato" (in base alla progettazione) di supportarlo nella loro classe XmlSerializer. Ma da allora è stato aggiunto alla classe di utilità XmlConvert.

XmlConvert.ToDateTimeOffset

XmlConvert.ToString

Dico di andare avanti e utilizzare DateTimeOffset e TimeZoneInfo a causa di tutti i vantaggi, basta fare attenzione quando si creano entità che saranno o potrebbero essere serializzate in o da XML (tutti gli oggetti business quindi).

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.