2013-04-10 18 views
6

Come si serializza correttamente un oggetto DateTime (ad esempio utilizzando un BinaryWriter) e si conserva il suo stato completo?Serialize DateTime as binary

Avevo l'impressione che una data fosse rappresentata solo da un intero interno lungo e che questo numero intero fosse accessibile come la proprietà Ticks di DateTime. Tuttavia, guardando l'attuazione, la proprietà zecche restituisce in realtà un sottoinsieme di dati interno vero e proprio che è memorizzato in un ulong chiamato dateData

zecche (che ottiene appena InternalTicks) è implementato in questo modo:

public long InternalTicks 
{ 
    get { return (long) this.dateData & 4611686018427387903L; } 
} 

Per quanto posso vedere, questo significa che dateData potrebbe contenere informazioni non rivelate dalla proprietà Ticks.

Stranger ancora, il BinaryFormatter serializzazione di un DateTime fa in GetObjectData():

info.AddValue("ticks", this.InternalTicks); 
info.AddValue("dateData", this.dateData); 

che sarà in uscita due anela nel flusso, dove uno di loro sarebbero facilmente recovererd dall'altro!

Come è possibile serializzare il mio DateTime senza il rischio di perdere nessuno degli stati interni, (preferibilmente ovviamente in soli 8 byte e senza riflessione). Sto pensando che forse può essere lanciato (non sicuro), direttamente a un ulong?

O mi preoccupo senza motivo, la proprietà Ticks codificherà effettivamente tutto lo stato necessario?

+1

Personalmente userei solo 'DateTim e' per le date 'UTC' e' DateTimeOffset' per le date locali. Quindi non devi preoccuparti di memorizzare il "Kind", è semplicemente sempre "UTC". – CodesInChaos

+0

Non controllo il tipo degli orari di data serializzati. –

risposta

8

Ci sono due tipi di informazioni di cui preoccuparsi:

  • zecche
  • Il DateTimeKind

Internamente questi sono entrambi codificati in un unico lungo, i dateData in questo modo:

this.dateData = (ulong) (ticks | (((long) kind) << 62)); 

Così la proprietà Ticks sarà non codifica tutto lo stato. Mancheranno le informazioni su DateTimeKind.

Il dateDatafa codificare tutti i dati, quindi è una cosa curiosa che i negozi serialiser sia che eTicks!

Quindi, cosa si potrebbe fare è questo:

ulong dataToSerialise = (ulong) (date.Ticks | ((long) date.Kind) << 62); 

E quando la deserializzazione, si può fare questo:

long ticks = (long)(deserialisedData & 0x3FFFFFFFFFFFFFFF); 
DateTimeKind kind = (DateTimeKind)(deserialisedData >> 62); 
DateTime date = new DateTime(ticks, kind); 

Questo fa fare uso di conoscenza circa la struttura interna di DateTime, e potrebbe teoricamente cambierà in futuro, che potrebbe rompere questo tipo di serializzazione.


EDIT

ci sono alcune cose da sapere a che fare con la regolazione dell'ora locale.

Quindi ho intenzione di suggerire che invece di pasticciare con tutto quanto sopra, si guardano le DateTime.ToBinary() e DateTime.FromBinary() metodi che vi permetterà di serializzare come una lunga, ferme restando le riserve relative alla locale regolazione del tempo. Questi avvertimenti sono completamente documentati nei link MSDN sopra.

+0

Grazie, la domanda però è: come scrivere il data private date date su un BinaryWriter. Cioè: come faccio a non correre un datetime all'ulong che è il suo stato interno? –

+0

Basta replicare cosa fa l'implementazione interna. Aggiornerò con un esempio in un minuto. –

+0

ah, vuoi dire o unire insieme i valori. Intelligente. –

5

ho fatto questo con la serializzazione per la data di trasmissione in Socket TCP

qui è il codice è possibile serializzare qualsiasi oggetto come questo

public static byte[] DateToBytes(DateTime _Date) 
{ 
    using (System.IO.MemoryStream MS = new System.IO.MemoryStream()) { 
     BinaryFormatter BF = new BinaryFormatter(); 
     BF.Serialize(MS, _Date); 
     return MS.GetBuffer(); 
    } 
} 


public static DateTime BytesToDate(byte[] _Data) 
{ 
    using (System.IO.MemoryStream MS = new System.IO.MemoryStream(_Data)) { 
     MS.Seek(0, SeekOrigin.Begin); 
     BinaryFormatter BF = new BinaryFormatter(); 
     return (DateTime)BF.Deserialize(MS); 
    } 
} 

EDIT

senza BinaryFormatter

//uses 8 byte 
DateTime tDate = DateAndTime.Now; 
long dtVal = tDate.ToBinary(); 
//64bit binary 

byte[] Bits = BitConverter.GetBytes(tDate.ToBinary()); 
//your byte output 

//reverse 
long nVal = BitConverter.ToInt64(Bits, 0); 
//get 64bit binary 
DateTime nDate = DateTime.FromBinary(nVal); 
//convert it to date 
+1

Forse avrebbe dovuto essere più chiaro: non voglio usare reflection, o binaryformatter, e la ragione è che deve essere veloce e usare solo 8 byte. –

+0

Puntelli per l'uso di BitConverter, nuova classe per me e soluzione semplice pulita. :-) –