Время шло в обратном направлении в 1890-х годах? Как объяснить удивительный график DateTime.ToOADate для отрицательных значений? - PullRequest
0 голосов
/ 05 июля 2018

Функция DateTime.ToOADate отображает значения DateTime в числа.

Можно ли упростить приведенную ниже функцию?

// drop time component
double DateComponent(double date) => DateTime.FromOADate(date).Date.ToOADate();

Разве это не эквивалентно Math.Floor? Я рассуждал. Однако, когда я тщательно протестировал (с помощью FsCheck), я обнаружил, что он дает разные результаты для отрицательных значений:

 > DateComponent(-4.1)
 -4
 > Math.Floor(-4.1)
 -5

Кажется, DateComponent округляется к нулю, а не к нулю.

Чтобы лучше понять, я построил график DateTime.ToOADate. Для положительных значений линия прямая и увеличивается со временем. Однако для отрицательных значений график кусочно уменьшается с разрывами влево при целочисленных значениях. Что происходит?

  1. Почему график такой? Это ошибка? Если по замыслу, почему? Время не бежало задом наперед в 1890-х годах. Функция ToOADate нарушает многие разумные предположения

    • f увеличивается со временем, т.е. t1
    • f непрерывно
  2. Можно ли упростить функцию DateComponent до чистой арифметики?

graph of DateTime.ToOADate

Ответы [ 2 ]

0 голосов
/ 06 июля 2018

Чтение справочного источника .NET, реализация отрицательных значений в специальном случае ToOADate и FromOADate.

https://github.com/Microsoft/referencesource/blob/3b1eaf5203992df69de44c783a3eda37d3d4cd10/mscorlib/system/datetime.cs#L548

// Converts an OLE Date to a tick count.
// This function is duplicated in COMDateTime.cpp
internal static long DoubleDateToTicks(double value) {
    // The check done this way will take care of NaN
    if (!(value < OADateMaxAsDouble) || !(value > OADateMinAsDouble))
        throw new ArgumentException(Environment.GetResourceString("Arg_OleAutDateInvalid"));

    // Conversion to long will not cause an overflow here, as at this point the "value" is in between OADateMinAsDouble and OADateMaxAsDouble
    long millis = (long)(value * MillisPerDay + (value >= 0? 0.5: -0.5));
    // The interesting thing here is when you have a value like 12.5 it all positive 12 days and 12 hours from 01/01/1899
    // However if you a value of -12.25 it is minus 12 days but still positive 6 hours, almost as though you meant -11.75 all negative
    // This line below fixes up the millis in the negative case
    if (millis < 0) {
        millis -= (millis % MillisPerDay) * 2;
    }

    millis += DoubleDateOffset / TicksPerMillisecond;

    if (millis < 0 || millis >= MaxMillis) throw new ArgumentException(Environment.GetResourceString("Arg_OleAutDateScale"));
    return millis * TicksPerMillisecond;
}
0 голосов
/ 05 июля 2018

Определение Дата автоматизации OLE"смешно":

Дата OLE-автоматизации реализована в виде числа с плавающей запятой, неотъемлемым компонентом которого является число дней до или после полуночи 30 декабря 1899 года, а дробный компонент представляет время этого дня, деленное на 24. Например, полночь 31 декабря 1899 года представлено 1,0; 6 утра, 1 января 1900 года представлено 2,25; полночь 29 декабря 1899 года представлена ​​-1,0; и 6 часов утра 29 декабря 1899 года - -1,25.

Так что это не простое расстояние между 0 и датой OLE. По правде говоря, это дата (целочисленная часть), «закодированная» вместе со временем (дробная часть). Это порождает интересный парадокс:

DateTime.FromOADate(0.1) == DateTime.FromOADate(-0.1)

В обоих случаях целочисленная часть равна 0, а дробная часть равна 0.1 :-) (в обоих случаях это 1899-12-30 02: 24: 00 )

Теперь ... Вы можете просто использовать Math.Truncate или привести к long для усечения значений.

static double DateComponent(double date) => Math.Truncate(date);

static double DateComponent(double date) => (long)date;

Более авторитетная страница , объясняющая формат дат OLE, где они приводят пример с 0,75 / -0,75.

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