Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serialize DateTime as binary

How do I properly serialize a DateTime object (e.g. using a BinaryWriter), and preserve its complete state?

I was under the impression that a date time was only represented by an internal long integer, and that this integer was accessible as the Ticks property of the DateTime. However, looking at the implementation, the Ticks property actually returns a subset of the real internal data which is stored in an ulong called dateData

Ticks (which just gets InternalTicks) is implemented like so:

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

As far as I can see this means the dateData may contain information that is not revealed by the Ticks property.

Stranger yet, the BinaryFormatter Serialization of a DateTime does this in GetObjectData():

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

That will output two longs in the stream, where one of them would be easily recovererd from the other!

How can I serialize my DateTime without risk of losing any of the internal state, (preferably of course in just 8 bytes, and without reflection). I'm thinking maybe it can be cast (unsafe), directly to an ulong?

Or am I worrying for no reason, will the Ticks property actually encode all the necessary state?

like image 840
Anders Forsgren Avatar asked Apr 10 '13 07:04

Anders Forsgren


People also ask

Can you serialize DateTime?

For serializing, you can use the DateTime(Offset). ToString method in your converter write logic. This method allows you to write DateTime and DateTimeOffset values using any of the standard date and time formats, and the custom date and time formats.

How do you serialize binary?

In binary serialization, the public and private fields of the object and the name of the class, including the assembly containing the class, are converted to a stream of bytes, which is then written to a data stream. When the object is subsequently deserialized, an exact clone of the original object is created.


2 Answers

There are two pieces of information to worry about:

  • The Ticks
  • The DateTimeKind

Internally these are both encoded into a single long, the dateData like so:

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

So the Ticks property will not encode all the state. It will be missing the DateTimeKind information.

The dateData does encode all the data, so it is a curious thing that the serialiser stores both that and Ticks!

So what you could do is this:

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

And when deserializing, you can do this:

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

This does make use of knowledge about the internals of DateTime, and it could theoretically change in the future, which could break this kind of serialisation.


EDIT

There are some gotchas to do with local time adjustment.

So I'm going to suggest that instead of messing about with all of the above, you look at the DateTime.ToBinary() and DateTime.FromBinary() methods which will allow you to serialize as a long, subject to the caveats relating to the local time adjustment. These caveats are fully documented in the MSDN links above.

like image 191
Matthew Watson Avatar answered Sep 28 '22 01:09

Matthew Watson


i have done this with serialization for transmitting date in TCP Sockets

here is code you can serialize any object like this

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

without 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 
like image 32
Jack Gajanan Avatar answered Sep 27 '22 23:09

Jack Gajanan