Сериализовать DateTime как двоичный

Как правильно сериализовать объект DateTime (например, с помощью BinaryWriter) и сохранить его полное состояние?

у меня сложилось впечатление, что время даты представлено только внутренним длинным целым числом, и что это целое число доступно как Ticks свойство DateTime. Однако, глядя на реализацию, свойство Ticks фактически возвращает подмножество реальных внутренних данных, которое хранится в ulong под названием dateData

галочки (которые just gets InternalTicks) реализуется следующим образом:

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

насколько я вижу, это означает, что dateData может содержать информацию, которая не раскрывается Ticks собственность.

еще более странно, что сериализация BinaryFormatter DateTime делает это в GetObjectData ():

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

это выведет два долго в потоке, где один из них будет легко recovererd от другого!

как сериализовать DateTime без риска потерять какое-либо внутреннее состояние (желательно, конечно, всего за 8 байт и без отражения). Я думаю, может быть, его можно бросить (небезопасно), прямо на улонг?

или я беспокоюсь без причины, будет Ticks свойство фактически кодирует все необходимое состояние?

2 ответов


есть две части информации, чтобы беспокоиться о:

  • Галочки
  • DateTimeKind

внутренне они оба закодированы в один длинный, dateData так:

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

так Ticks недвижимость не кодировать все государство. В нем будет отсутствовать информация DateTimeKind.

The 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, и это теоретически может измениться в будущем, что может нарушить этот вид сериализации.


редактировать

есть некоторые gotchas делать с местным временем регулировка.

поэтому я собираюсь предложить, чтобы вместо того, чтобы возиться со всем вышеперечисленным, вы посмотрели на DateTime.ToBinary() и DateTime.FromBinary() методы будет позволяет сериализовать как длинный, с учетом предостережений, связанных с корректировкой местного времени. Эти предостережения полностью документированы в ссылках MSDN выше.


Я сделал это с сериализацией для передачи даты в сокетах 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);
    }
}

редактировать

без 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