.NET DateTime, другое разрешение при конвертации в OADate и обратно? - PullRequest
7 голосов
/ 16 сентября 2010

Я конвертирую DateTime в OADate. Я ожидал получить точно такой же DateTime при преобразовании OADate, но теперь он имеет разрешение только в миллисекундах и поэтому отличается.

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 явно достаточно хорошее.

Ответы [ 3 ]

10 голосов
/ 18 декабря 2012

Я думаю, это отличный вопрос.(Я только что обнаружил это.)

Если вы не работаете с датами, близкими к 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 мкс.

Вблизи даты и времени, которые мы рассматриваем, точность даты OA составляет:

Math.Pow(2.0, -37.0) дней, или около 0.6286 мкс

Мы пришли к выводу, что в этом регионе a DateTime является более точным, чем дата OA на(чуть более) в шесть раз.

Давайте преобразуем 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 мкс.Мы ожидаем, что потеря точности будет в пределах ± 0,4 мкс, поскольку этот интервал имеет длину 0,8 мкс, что сравнимо с упомянутыми ранее 0,6286 мкс.

Если мы пойдем другим путем, начнем с double, представляющегоДата ОА не слишком близка к 1900 году, и сначала используют FromOADatePrecise и затем ToOADatePrecise, затем мы возвращаемся к double, и поскольку точностьпромежуточное значение DateTime превосходит значение даты OA, в этом случае мы ожидаем идеального двустороннего приема.Если, с другой стороны, вы используете методы BCL FromOADate и ToOADate в одном и том же порядке, крайне маловероятно получить хорошее двустороннее сообщение (если только double, с которого мы начинали, не имеет совершенно особой формы).

3 голосов
/ 16 сентября 2010

Статический метод, вызываемый ToOADate, четко делит тики на 10000, а затем сохраняет результат в длинном, удаляя, таким образом, любую информацию с точностью до миллисекунды.

Кто-нибудь знает, где найти спецификации формата 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);
    }
1 голос
/ 16 сентября 2010

Возможно, что-то связано с точностью до двойного, а не с DateTime.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...