6

如何正确序列化 DateTime 对象(例如使用 BinaryWriter),并保留其完整状态?

我的印象是日期时间仅由内部长整数表示,并且该整数可作为TicksDateTime 的属性访问。但是,查看实现,Ticks 属性实际上返回了实际内部数据的子集,该子集存储在名为 ulong 的dateData

Ticks(它只是获取 InternalTicks)是这样实现的:

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

据我所知,这意味着 dateData 可能包含该Ticks属性未显示的信息。

更奇怪的是,DateTime 的 BinaryFormatter 序列化在 GetObjectData() 中执行此操作:

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

这将在流中输出两个long,其中一个很容易从另一个中恢复!

我怎样才能序列化我的 DateTime 而不会丢失任何内部状态(当然最好只有 8 个字节,并且没有反射)。我在想也许它可以直接投射到 ulong 上(不安全)?

还是我无缘无故地担心,该Ticks属性实际上会编码所有必要的状态吗?

4

2 回答 2

12

有两条信息需要担心:

  • 蜱虫
  • 日期时间种类

在内部,它们都被编码为一个长整数,dateData 如下所示:

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

因此该Ticks属性不会对所有状态进行编码。它将缺少 DateTimeKind 信息。

dateData 确实对所有数据进行了编码,因此序列化程序同时存储该数据 Ticks!

所以你可以做的是:

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

反序列化时,您可以这样做:

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

这确实利用了有关 DateTime 内部的知识,并且它在未来理论上可能会发生变化,这可能会破坏这种序列化。


编辑

本地时间调整有一些问题。

因此,我建议您不要搞乱上述所有内容,而是查看DateTime.ToBinary()andDateTime.FromBinary()方法,这些方法允许您将序列化为 long,但要遵守与本地时间调整相关的警告。这些警告在上面的 MSDN 链接中有完整的记录。

于 2013-04-10T07:44:26.227 回答
5

我已经通过序列化在 TCP 套接字中传输日期来做到这一点

这是您可以像这样序列化任何对象的代码

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

编辑

没有二进制格式化程序

//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 
于 2013-04-10T08:07:32.813 回答