Sérialiser DateTime en binary

Comment sérialiser un object DateTime (par exemple en utilisant un BinaryWriter) et conserver son état complet

J’avais l’impression qu’une date n’était représentée que par un entier long interne et que cet entier était accessible en tant que propriété Ticks du DateTime. Toutefois, si l’on examine l’implémentation, la propriété Ticks renvoie en réalité un sous-ensemble des données internes réelles stockées dans un ulong appelé dateData

Ticks (qui obtient juste InternalTicks) est implémenté comme suit:

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

Autant que je sache, cela signifie que dateData peut contenir des informations qui ne sont pas révélées par la propriété Ticks .

Plus étrange encore, la sérialisation BinaryFormatter d’un DateTime fait cela dans GetObjectData ():

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

Cela produira deux longs dans le stream, où l’un d’eux serait facilement récupéré de l’autre!

Comment puis-je sérialiser mon DateTime sans risquer de perdre l’état interne (de préférence bien sûr en 8 octets et sans reflection). Je pense peut-être que ça peut être lancé (dangereux), directement sur un ulong?

Ou suis-je inquiet pour aucune raison, la propriété Ticks encodera-t-elle réellement tout l’état nécessaire?

Il y a deux informations à prendre en compte:

  • Les tiques
  • Le DateTimeKind

En interne, ils sont tous deux encodés dans un seul et même long, dateData, comme ceci:

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

La propriété Ticks ne va donc pas encoder tout l'état. Il manquera les informations DateTimeKind.

dateData encode toutes les données, il est donc curieux que le sérialiseur stocke à la fois cela et les Ticks !

Donc, ce que vous pourriez faire est ceci:

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

Et lors de la désérialisation, vous pouvez faire ceci:

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

Cela utilise les connaissances sur les composants internes de DateTime et pourrait théoriquement changer dans le futur, ce qui pourrait briser ce type de sérialisation.


MODIFIER

L'ajustement de l'heure locale présente certains inconvénients.

Donc, je vais suggérer qu'au lieu de jouer avec tout ce qui précède, vous regardez les méthodes DateTime.ToBinary() et DateTime.FromBinary() qui vous permettront de sérialiser longuement, sous réserve des mises en garde relatives à l'ajustement de l'heure locale. Ces mises en garde sont entièrement documentées dans les liens MSDN ci-dessus.

Je l’ai fait avec la sérialisation pour la transmission de la date dans les sockets TCP

voici le code que vous pouvez sérialiser tout object comme celui-ci

 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); } } 

MODIFIER

sans format binary

 //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