10

我正在将 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

报价来自:634202170964319073

来自 b 的报价:634202170964310000

OADate 双倍:40437.290467951389

这是什么原因?DateTime 的分辨率显然已经足够好了。

4

3 回答 3

15

我认为这是一个很好的问题。(我刚刚发现它。)

除非您使用非常接近 1900 年的日期进行操作,否则 aDateTime将具有比 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

any 的精度DateTime为 0.1 µs。

在我们考虑的日期和时间附近,OA 日期的精度为:

Math.Pow(2.0, -37.0)天,或大约0.6286µs

我们得出的结论是,在该区域中, aDateTime比 OA 日期更精确(刚好超过)六倍。

让我们转换ourDTdouble使用我上面的扩展方法

double ourOADate = ourDT.ToOADatePrecise();
// .ToString("G") gives 40437.2904679619
// .ToString("R") gives 40437.290467961888

现在,如果您使用上面的静态方法转换ourOADate回 a ,您会得到DateTimeFromOADatePrecise

2010-09-16T06:58:16.4319072(用"O"格式写)

与原始数据相比,我们看到在这种情况下精度损失为 0.1 µs。我们预计精度损失在 ±0.4 µs 内,因为该间隔的长度为 0.8 µs,与前面提到的 0.6286 µs 相当。

如果我们换一种方式,从double表示 OA 日期不太接近 1900 年的a 开始,使用FromOADatePrecise然后 ToOADatePrecise使用 ,然后回到 a double,因为中间DateTime值的精度优于 OA日期,我们期望在这种情况下完美的往返。另一方面,如果您使用 BCL 方法FromOADateToOADate以相同的顺序,则极不可能获得良好的往返(除非double我们开始使用的形式非常特殊)。

于 2012-12-17T21:04:58.857 回答
4

ToOADate 调用的静态方法清楚地将刻度除以 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);
    }
于 2010-09-16T07:29:35.987 回答
1

可能与双精度有关,而不是与日期时间有关。

于 2010-09-16T06:59:23.427 回答