我正在将DateTime转换为OADate。我希望在将OADate转换回时获得完全相同的DateTime,但现在它的分辨率只有毫秒,因此不同。
var a = DateTime.UtcNow;
double oadate = a.ToOADate();
var b = DateTime.FromOADate(oadate);
int compare = DateTime.Compare(a, b);
//Compare is not 0; the date times are not the same
来自a:634202170964319073
来自b的哨声:634202170964310000
OADate双倍:40437.290467951389
这是什么原因? DateTime的分辨率显然足够好。
答案 0 :(得分:10)
我认为这是一个很好的问题。 (我刚发现它。)
除非您的日期非常接近1900年,否则DateTime
的 精度高于OA日期。但是由于一些不明原因,DateTime
结构的作者只需爱就可以在DateTime
和其他内容之间进行转换时截断到最接近的整数毫秒。毋庸置疑,这样做会毫无理由地抛弃很多精确度。
这是一个解决方法:
static readonly DateTime oaEpoch = new DateTime(1899, 12, 30);
public static DateTime FromOADatePrecise(double d)
{
if (!(d >= 0))
throw new ArgumentOutOfRangeException(); // NaN or negative d not supported
return oaEpoch + TimeSpan.FromTicks(Convert.ToInt64(d * TimeSpan.TicksPerDay)):
}
public static double ToOADatePrecise(this DateTime dt)
{
if (dt < oaEpoch)
throw new ArgumentOutOfRangeException();
return Convert.ToDouble((dt - oaEpoch).Ticks) / TimeSpan.TicksPerDay;
}
现在,让我们考虑(根据您的问题)DateTime
给出:
var ourDT = new DateTime(634202170964319073);
// .ToSting("O") gives 2010-09-16T06:58:16.4319073
任何DateTime
的精度为0.1μs。
在我们考虑的日期和时间附近,OA日期的精确度为:
Math.Pow(2.0, -37.0)
天,或大约0.6286
μs
我们得出结论,在此区域 a DateTime
比OA日期更精确(仅仅过了)6倍。
让我们使用上面的扩展方法将ourDT
转换为double
double ourOADate = ourDT.ToOADatePrecise();
// .ToString("G") gives 40437.2904679619
// .ToString("R") gives 40437.290467961888
现在,如果您使用上面的静态ourOADate
方法将DateTime
转换回FromOADatePrecise
,那么
2010-09-16T06:58:16.4319072
(以"O"
格式编写)
与原始相比,我们发现在这种情况下精度损失为0.1μs。我们预计精度损失在±0.4μs内,因为该间隔的长度为0.8μs,与前面提到的0.6286μs相当。
如果我们采取相反的方式,从代表OA日期的double
开始并不太接近1900年,首先使用FromOADatePrecise
和那么 ToOADatePrecise
,然后我们回到double
,因为中间DateTime
的精度优于OA日期的精度,我们期望一个完美的圆 - 在这种情况下旅行。另一方面,如果您以相同的顺序使用BCL方法FromOADate
和ToOADate
,那么获得良好的往返是极不可能的(除非我们开始double
有一个非常特殊的形式)。
答案 1 :(得分:3)
ToOADate调用的静态方法清楚地将ticks除以10000,然后将结果存储为long,从而删除任何亚毫秒信息
有谁知道在哪里可以找到OADate格式的规格?
private static double TicksToOADate(long value)
{
if (value == 0L)
{
return 0.0;
}
if (value < 0xc92a69c000L)
{
value += 0x85103c0cb83c000L;
}
if (value < 0x6efdddaec64000L)
{
throw new OverflowException(Environment.GetResourceString("Arg_OleAutDateInvalid"));
}
long num = (value - 0x85103c0cb83c000L) / 0x2710L;
if (num < 0L)
{
long num2 = num % 0x5265c00L;
if (num2 != 0L)
{
num -= (0x5265c00L + num2) * 2L;
}
}
return (((double)num) / 86400000.0);
}
答案 2 :(得分:1)
可能与double的精度有关,而不是DateTime。