2013-04-10 36 views
6

Làm cách nào để tuần tự hóa đúng đối tượng DateTime (ví dụ: sử dụng BinaryWriter) và giữ nguyên trạng thái đầy đủ của nó?Serialize DateTime dưới dạng nhị phân

Tôi đã có ấn tượng rằng thời gian ngày chỉ được biểu diễn bằng số nguyên dài bên trong và số nguyên này có thể truy cập được như thuộc tính Ticks của DateTime. Tuy nhiên, nhìn vào thực hiện, tài sản Bọ ve thực sự trả về một tập hợp con của dữ liệu nội bộ thực được lưu trữ trong một ulong gọi dateData

Bọ ve (mà chỉ được InternalTicks) được thực hiện như sau:

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

Theo tôi có thể thấy điều này có nghĩa là dateData có thể chứa thông tin không được tiết lộ bởi thuộc tính Ticks.

Stranger Chưa hết, BinaryFormatter serialization của một DateTime thực hiện điều này trong GetObjectData():

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

Đó sẽ ra hai chờ đợi trong dòng, nơi một trong số họ sẽ được dễ dàng recovererd từ khác!

Làm cách nào để có thể tuần tự hóa DateTime của mình mà không có nguy cơ mất bất kỳ trạng thái nội bộ nào (tốt nhất là chỉ trong 8 byte và không có phản ánh). Tôi nghĩ có lẽ nó có thể được đúc (không an toàn), trực tiếp đến một ulong?

Hoặc tôi lo lắng không có lý do gì, thuộc tính Ticks có thực sự mã hóa tất cả trạng thái cần thiết không?

+1

Cá nhân tôi chỉ sử dụng 'DateTim e' cho 'UTC' ngày và' DateTimeOffset' cho ngày địa phương. Sau đó, bạn không cần phải lo lắng về việc lưu trữ 'Loại', nó chỉ đơn giản là luôn luôn' UTC'. – CodesInChaos

+0

Tôi không kiểm soát Loại ngày được đăng theo thứ tự. –

Trả lời

8

Có hai mẩu thông tin lo lắng về:

  • Các Bọ ve
  • Các DateTimeKind

Bên trong những đều được mã hóa thành một dài duy nhất, dateData như vậy:

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

Vì vậy, thuộc tính Ticks sẽ không mã hóa tất cả trạng thái. Nó sẽ thiếu thông tin DateTimeKind.

Các dateDatakhông mã hóa tất cả dữ liệu, vì vậy nó là một điều tò mò rằng các cửa hàng serialiser cả mà Ticks!

Vì vậy, những gì bạn có thể làm là thế này:

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

Và khi deserializing, bạn có thể làm điều này:

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

này không tận dụng kiến ​​thức gì về thiết kế của DateTime, và nó có thể về mặt lý thuyết thay đổi trong tương lai, có thể phá vỡ loại serialization này.


EDIT

Có một số gotchas để làm với điều chỉnh giờ địa phương.

Vì vậy, tôi sẽ đề nghị rằng thay vì rối tung lên với tất cả những điều trên, bạn nhìn vào các phương pháp DateTime.ToBinary()DateTime.FromBinary()sẽ cho phép bạn serialize như một chặng đường dài, tùy thuộc vào những hãy cẩn thận liên quan đến địa phương Điều chỉnh thời gian. Những cảnh báo này được ghi chép đầy đủ trong các liên kết MSDN ở trên.

+0

Cảm ơn, câu hỏi là: cách viết dateData trường riêng thành một BinaryWriter. Đó là: làm thế nào để tôi không an toàn đúc một datetime đến ulong đó là trạng thái nội bộ của nó? –

+0

Chỉ cần sao chép nội dung triển khai nội bộ. Tôi sẽ cập nhật với một ví dụ trong một phút. –

+0

ah, ý bạn là nhập các giá trị vào nhau. Tài giỏi. –

5

tôi đã làm điều này với serialization cho ngày truyền trong TCP Sockets

đây là mã bạn có thể serialize bất kỳ đối tượng như thế này

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

mà không 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

Nên có lẽ đã rõ ràng hơn: Tôi không muốn sử dụng sự phản chiếu, hoặc nhị phân định dạng, và lý do là nó cần phải nhanh và chỉ sử dụng 8 byte. –

+0

Đạo cụ cho việc sử dụng BitConverter, lớp học mới cho tôi và giải pháp đơn giản. :-) –

Các vấn đề liên quan