2013-04-10 18 views
6

(BinaryWriterを使用して)DateTimeオブジェクトを適切にシリアル化し、完全な状態を保持するにはどうすればよいですか?バイナリとしてDateTimeをシリアライズ

私は、日付時間が内部の長い整数で表され、この整数がDateTimeのTicksプロパティとしてアクセス可能であったという印象を受けました。

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

:ただし、実装を見て、マダニプロパティが実際ULONGに格納されている実際の内部データのサブセットがdateData

ティック(ちょうどInternalTicksを取得する)ように同様に実装されていると呼ばれる戻り私が見る限り、これは、dateDataにはTicksプロパティによって明らかにされない情報が含まれている可能性があることを意味します。

ストレンジャーまだ、日時のにBinaryFormatter直列化は、GetObjectData()でこれを行います。出力は以下となりますストリームで long型は、そのうちの一つは、簡単に他からrecovererdされるだろう

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

どのように内部状態を失うことなく(もちろん、ちょうど8バイトで、反射なしで)DateTimeをシリアル化できますか?私はおそらくそれがulongに直接(安全ではない)キャストできると思っていますか?

また、理由がないと心配していますが、Ticksプロパティは実際に必要な状態をすべてエンコードしますか?

+1

個人的に私は 'DateTimローカル日付の場合は 'DateTimeOffset'となります。 'Kind'を保存することを心配する必要はありません。それは単に' UTC'です。 – CodesInChaos

+0

シリアル化された日付時刻の種類は制御しません。 –

答えて

8

心配する二つの情報があります。

  • ダニ
  • 内部これらは両方ともそのような単一の長い、dateDataにエンコードされているDateTimeKind

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

したがってTicksプロパティはになりませんはすべての状態をエンコードします。 DateTimeKind情報が失われます。

dateDataエンコードデータをすべて行いますので、シリアライザストアは両方Ticksそのことに好奇心旺盛なことです!

だから何あなたができることはこれです:これは日時の内部に関する知識を利用することなく、それは可能性が

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

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

そしてデシリアライズするとき、あなたがこれを行うことができます理論的には将来変化し、この種のシリアライゼーションを破る可能性があります。


EDIT

現地時間調整を行うには、いくつかの落とし穴があります。

だから私は、代わりに上記のすべてとぐちゃぐちゃで、あなたはローカルに関連する警告を受け、あなたが長いとしてシリアライズすることができますDateTime.ToBinary()DateTime.FromBinary()方法を見てみることをお奨めするつもりです時間調整。これらの警告は、上記のMSDNリンクに完全に記載されています。

+0

ありがとうございますが、問題は:プライベートフィールドdateDataをBinaryWriterに書き込む方法です。つまり、私は内部状態であるulongにdatetimeをキャストすることをどのように危険にしますか? –

+0

内部実装が何を行うのかを複製するだけです。私は1分で例を挙げて更新します。 –

+0

ああ、値を一緒に使うことを意味します。賢い –

5

私はTCPソケットここ

に日付を送信するためのシリアル化でこれを行っているが、あなたがに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 
せずに、この

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

のような任意のオブジェクトをシリアル化できるコードです

+1

おそらくより明確になっているはずです:私はリフレクションやバイナリフォーマッタを使用したくないので、高速で8バイトしか使用しなくてはならないという理由があります。 –

+0

BitConverterの使用のための小道具、私に新しいクラスとクリーンなシンプルなソリューション。 :-) –

関連する問題