Serialize DateTime as binary

橙三吉。 提交于 2019-12-21 02:34:49

问题


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?


回答1:


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.




回答2:


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 


来源:https://stackoverflow.com/questions/15919598/serialize-datetime-as-binary

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!