如何将 DateTimeOffset 序列化为二进制流
How to serialize a DateTimeOffset into a binary stream
我想知道将 DateTimeOffset
序列化为二进制流(使用 BinaryWriter
)并再次反序列化(使用 BinaryReader
)的最佳方法是什么。
要序列化 DateTime
,我有:
public void WriteValue(DateTime value)
{
_writer.Write(value.ToBinary());
}
和
public DateTime ReadDateTime()
{
return DateTime.FromBinary(_reader.ReadInt64());
}
在性能和存储大小方面 serialize/deserialize DateTimeOffset
的最佳方法是什么?
根据 Hans Passant 的评论,我提出了以下解决方案。要序列化:
public void WriteValue(DateTimeOffset value)
{
WriteValue(value.DateTime);
WriteValue((short)value.Offset.TotalMinutes);
}
并反序列化:
public DateTimeOffset ReadDateTimeOffset()
{
var dateTime = ReadDateTime();
var minutes = ReadInt16();
return new DateTimeOffset(dateTime, TimeSpan.FromMinutes(minutes));
}
所以这些方法调用 DateTime
的现有序列化方法,如问题中所述。
我仍然想知道这是否是最有效的方法。调用 TotalMinutes
和 TimeSpan.FromMinutes
有多快?
序列化和反序列化日期时间对象(任何语言)的正确方法是使用时区和夏令时独立值,例如 UTC 时间。在反序列化时,要么保持 UTC(如果仅由代码使用),要么选择当前本地时区偏移量(如果由人类读取)。这样可以避免不同时区的问题,即使值保持在一个国家/地区,也可以避免夏令时在 serialization/deserialization.
之间变化时出现问题
DateTimeOffset.UtcTicks
属性(64 位整数)是二进制序列化程序的一个很好的候选者。注意不要使用 DateTimeOffset.Ticks
,因为 属性 包含任何偏移量。
DateTimeOffset dto = DateTimeOffset.Now;
using (var w = new BinaryWriter(...))
{
w.Write(dto.UtcTicks); // do not use dto.Ticks!
}
反序列化看起来很笨拙,因为没有简单的方法可以从 UTC 刻度创建 DateTimeOffset
。如果该对象从未向人类展示,您可以跳过到当地时间的转换。
using (var r = new BinaryReader(...))
{
long utcTicks = r.ReadInt64();
dto = new DateTimeOffset(utcTicks, TimeSpan.Zero).ToLocalTime();
}
在存储方面,您需要 8 个字节 = 64 位。我不确定反序列化性能(未测试),但我认为它应该尽可能快。
编辑:要保留时区(偏移量)信息,
DateTimeOffset dto = DateTimeOffset.Now;
using (var w = new BinaryWriter(...))
{
w.Write(dto.Ticks);
w.Write(dto.Offset.Ticks);
}
using (var r = new BinaryReader(...))
{
long ticks = r.ReadInt64();
long offsetTicks = r.ReadInt64();
dto = new DateTimeOffset(ticks, new TimeSpan(offsetTicks);
}
并不是说我的回答增加了很多,但我已经编写了这个用于打包和解包 DateTimeOffset 的小实用程序:
public static class DateTimeOffsetExtensions
{
/// <summary>
/// Packs DateTimeOffset to bytes
/// </summary>
/// <param name="dateTimeOffset"></param>
/// <returns>10 byte packed bytearray</returns>
public static byte[] GetBytes(this DateTimeOffset dateTimeOffset)
{
return BitConverter.GetBytes(dateTimeOffset.Ticks).
Concat(BitConverter.GetBytes((Int16)dateTimeOffset.Offset.TotalMinutes)).ToArray();
}
/// <summary>
/// Reads 10 bytes from a buffer and turns back to DateTimeOffset
/// </summary>
/// <param name="bytes">Buffer</param>
/// <param name="offset">Offset to read from</param>
/// <returns></returns>
public static DateTimeOffset FromBytes(byte[] bytes, int offset)
{
var ticks = BitConverter.ToInt64(bytes, offset);
var offsetMinutes = BitConverter.ToInt16(bytes, offset + 8);
return new DateTimeOffset(ticks, TimeSpan.FromMinutes(offsetMinutes));
}
}
我想知道将 DateTimeOffset
序列化为二进制流(使用 BinaryWriter
)并再次反序列化(使用 BinaryReader
)的最佳方法是什么。
要序列化 DateTime
,我有:
public void WriteValue(DateTime value)
{
_writer.Write(value.ToBinary());
}
和
public DateTime ReadDateTime()
{
return DateTime.FromBinary(_reader.ReadInt64());
}
在性能和存储大小方面 serialize/deserialize DateTimeOffset
的最佳方法是什么?
根据 Hans Passant 的评论,我提出了以下解决方案。要序列化:
public void WriteValue(DateTimeOffset value)
{
WriteValue(value.DateTime);
WriteValue((short)value.Offset.TotalMinutes);
}
并反序列化:
public DateTimeOffset ReadDateTimeOffset()
{
var dateTime = ReadDateTime();
var minutes = ReadInt16();
return new DateTimeOffset(dateTime, TimeSpan.FromMinutes(minutes));
}
所以这些方法调用 DateTime
的现有序列化方法,如问题中所述。
我仍然想知道这是否是最有效的方法。调用 TotalMinutes
和 TimeSpan.FromMinutes
有多快?
序列化和反序列化日期时间对象(任何语言)的正确方法是使用时区和夏令时独立值,例如 UTC 时间。在反序列化时,要么保持 UTC(如果仅由代码使用),要么选择当前本地时区偏移量(如果由人类读取)。这样可以避免不同时区的问题,即使值保持在一个国家/地区,也可以避免夏令时在 serialization/deserialization.
之间变化时出现问题DateTimeOffset.UtcTicks
属性(64 位整数)是二进制序列化程序的一个很好的候选者。注意不要使用 DateTimeOffset.Ticks
,因为 属性 包含任何偏移量。
DateTimeOffset dto = DateTimeOffset.Now;
using (var w = new BinaryWriter(...))
{
w.Write(dto.UtcTicks); // do not use dto.Ticks!
}
反序列化看起来很笨拙,因为没有简单的方法可以从 UTC 刻度创建 DateTimeOffset
。如果该对象从未向人类展示,您可以跳过到当地时间的转换。
using (var r = new BinaryReader(...))
{
long utcTicks = r.ReadInt64();
dto = new DateTimeOffset(utcTicks, TimeSpan.Zero).ToLocalTime();
}
在存储方面,您需要 8 个字节 = 64 位。我不确定反序列化性能(未测试),但我认为它应该尽可能快。
编辑:要保留时区(偏移量)信息,
DateTimeOffset dto = DateTimeOffset.Now;
using (var w = new BinaryWriter(...))
{
w.Write(dto.Ticks);
w.Write(dto.Offset.Ticks);
}
using (var r = new BinaryReader(...))
{
long ticks = r.ReadInt64();
long offsetTicks = r.ReadInt64();
dto = new DateTimeOffset(ticks, new TimeSpan(offsetTicks);
}
并不是说我的回答增加了很多,但我已经编写了这个用于打包和解包 DateTimeOffset 的小实用程序:
public static class DateTimeOffsetExtensions
{
/// <summary>
/// Packs DateTimeOffset to bytes
/// </summary>
/// <param name="dateTimeOffset"></param>
/// <returns>10 byte packed bytearray</returns>
public static byte[] GetBytes(this DateTimeOffset dateTimeOffset)
{
return BitConverter.GetBytes(dateTimeOffset.Ticks).
Concat(BitConverter.GetBytes((Int16)dateTimeOffset.Offset.TotalMinutes)).ToArray();
}
/// <summary>
/// Reads 10 bytes from a buffer and turns back to DateTimeOffset
/// </summary>
/// <param name="bytes">Buffer</param>
/// <param name="offset">Offset to read from</param>
/// <returns></returns>
public static DateTimeOffset FromBytes(byte[] bytes, int offset)
{
var ticks = BitConverter.ToInt64(bytes, offset);
var offsetMinutes = BitConverter.ToInt16(bytes, offset + 8);
return new DateTimeOffset(ticks, TimeSpan.FromMinutes(offsetMinutes));
}
}