2013-04-10 2 views
6

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

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

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

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

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

Незнакомец все же, BinaryFormatter Сериализация DateTime делает это в GetObjectData():

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

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

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

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

+1

Лично я использовал бы только 'DateTim e' для дат 'UTC' и' DateTimeOffset' для местных дат. Тогда вам не нужно беспокоиться о сохранении 'Kind', это просто всегда' UTC'. – CodesInChaos

+0

Я не контролирую тип даты, сериализованный. –

ответ

8

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

  • отметками
  • The DateTimeKind

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

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

Таким образом, имущество Ticks будет не кодирует все состояние. Он не получит информацию о DateTimeKind.

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


EDIT

Есть несколько подводных камней, чтобы сделать с локальной регулировкой времени.

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

+0

Спасибо, вопрос в том, как написать частное поле dateData для BinaryWriter. То есть: как я небезопасно бросать datetime в ulong, являющееся его внутренним состоянием? –

+0

Просто повторите то, что делает внутренняя реализация. Через минуту я обновлю пример. –

+0

ах, вы имеете в виду общие значения. Умная. –

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

EDIT

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

Возможно, было более ясно: я не хочу использовать отражение или двоичный формат, и причина в том, что он должен быть быстрым и использовать только 8 байтов. –

+0

Подставки для использования BitConverter, новый класс для меня и чистое простое решение. :-) –

Смежные вопросы