.NET Framework中的DateTime类型的二进制表示和JavaScript中的Date类型互转
在.NET Framework中,DateTime类型是表示日期和时间值的结构体。它可以存储从公元1年1月1日到公元9999年12月31日之间的日期和时间。
而在JavaScript中,Date类型也是表示日期和时间值的对象。它可以存储从1970年1月1日 00:00:00 UTC到某个日期时间点之间的毫秒数。
在实际开发中,我们可能需要将.NET Framework中的DateTime类型的值转换为JavaScript中的Date类型的值,或者将JavaScript中的Date类型的值转换为.NET Framework中的DateTime类型的值。这个过程中,需要用到二进制表示的转换方法。
.NET Framework中的DateTime类型的二进制表示
在.NET Framework中,DateTime类型的值其实是一个64位整数,其中01-62位表示自公元1年1月1日0时0分0秒以来经过的100纳秒数,后63-64位则标识时间类型枚举。其中01(bit64 bit63),标识UTC时间。
// The data is stored as an unsigned 64-bit integer
// Bits 01-62: The value of 100-nanosecond ticks where 0 represents 1/1/0001 12:00am, up until the value
// 12/31/9999 23:59:59.9999999
// Bits 63-64: A four-state value that describes the DateTimeKind value of the date time, with a 2nd
// value for the rare case where the date time is local, but is in an overlapped daylight
// savings time hour and it is in daylight savings time. This allows distinction of these
// otherwise ambiguous local times and prevents data loss when round tripping from Local to
// UTC time.
private readonly ulong _dateData;
public long ToBinary()
{
if ((_dateData & KindLocal) != 0)
{
// Local times need to be adjusted as you move from one time zone to another,
// just as they are when serializing in text. As such the format for local times
// changes to store the ticks of the UTC time, but with flags that look like a
// local date.
// To match serialization in text we need to be able to handle cases where
// the UTC value would be out of range. Unused parts of the ticks range are
// used for this, so that values just past max value are stored just past the
// end of the maximum range, and values just below minimum value are stored
// at the end of the ticks area, just below 2^62.
TimeSpan offset = TimeZoneInfo.GetLocalUtcOffset(this, TimeZoneInfoOptions.NoThrowOnInvalidTime);
long ticks = Ticks;
long storedTicks = ticks - offset.Ticks;
if (storedTicks < 0)
{
storedTicks = TicksCeiling + storedTicks;
}
return storedTicks | (unchecked((long)KindLocal));
}
else
{
return (long)_dateData;
}
}
具体地,我们可以使用以下代码获取任意一个DateTime类型的值的二进制表示:
DateTime dateTime = DateTime.Now;
//转换二进制表示
var bin = dateTime.ToBinary();
//二进制标识转换为DateTime
var dateTime = System.DateTime.FromBinary(5249903872152447175);
JavaScript中的Date类型的二进制表示
JavaScript中的Date类型的值是一个64位浮点数,其中前53位表示毫秒数,后11位表示毫秒数的小数部分。
二进制表示的互转
将.NET Framework中的DateTime类型的二进制表示转换为JavaScript中的Date类型的值,我们需要先将前62位和后2位分离出来,再按照指定格式进行拼接。需要注意的是具体地,我们可以使用以下代码实现:
function fromDateTimeBinary(value) {
var UNIX_EPOCH = 62135596800000n;
//注意使用BigInt
//去掉63 64位,将时间默认为UTC
value = (BigInt(value) & 0x3fffffffffffffffn) // value & 0x3fffffffffffffff;
value = value / 10000n;
value = Number(value - UNIX_EPOCH)
return new Date(Number(value));
}
function toDateTimeBinary(date) {
var ticks = (BigInt(date.getTime()) * 10000n) + 621355968000000000n;
//将标志位置为UTC时间
ticks = (BigInt(ticks) | 0x4000000000000000n)
//注意返回的是个BigInt
return ticks;
}
//测试
console.log(fromDateTimeBinary(5249898290583602010));
console.log(toDateTimeBinary(new Date()));
//标准输出:
//2023-06-01T14:40:15.621Z
//5249903898125757904n