Подтвердить что ты не робот

Преобразовать DateTime в Julian Date в С# (ToOADate Safe?)

Мне нужно преобразовать из стандартной григорианской даты в число Юлиана.

Я ничего не видел на С#, чтобы сделать это напрямую, но я нашел много сообщений (в то время как Googling), предлагая использовать ToOADate.

Документация на ToOADate не предлагает это как действительный метод преобразования для юлианских дат.

Может ли кто-нибудь уточнить, будет ли эта функция выполнять точное преобразование или, возможно, более подходящим способом конвертировать DateTime в форматированную строку Юлиана.


Это дает мне ожидаемое число при проверке с Wikipedia Julian Day страница

public static long ConvertToJulian(DateTime Date)
{
    int Month = Date.Month;
    int Day = Date.Day;
    int Year = Date.Year;

    if (Month < 3)
    {
        Month = Month + 12;
        Year = Year - 1;
    }
    long JulianDay = Day + (153 * Month - 457) / 5 + 365 * Year + (Year / 4) - (Year / 100) + (Year / 400) + 1721119;
    return JulianDay;
}

Однако это не понимает используемые магические числа.

Спасибо


Литература:

4b9b3361

Ответ 1

OADate похож на Julian Dates, но использует другую отправную точку (30 декабря 1899 г. по сравнению с 1 января 4713 г. до н.э.) и другой пункт "нового дня". Джулиан Датс считает, что полдень станет началом нового дня, OADates используют современное определение, полночь.

Юлиан. Дата полуночи, 30 декабря 1899 года, составляет 2415018,5. Этот метод должен дать вам правильные значения:

public static double ToJulianDate(this DateTime date)
{
    return date.ToOADate() + 2415018.5;
}

Что касается алгоритма:

  • if (Month < 3) ...: Чтобы магические числа работали по праву, они помещают февраль в конец года.
  • (153 * Month - 457) / 5: Вау, это некоторые серьезные магические числа.
    • Обычно количество дней в каждом месяце составляет 31 28 31 30 31 30 31 31 30 31 30 31, но после этой корректировки в отчете if это становится 31 30 31 30 31 31 30 31 30 31 31 28. Или, вычтите 30, и вы получите 1 0 1 0 1 1 0 1 0 1 1 -2. Они создают этот шаблон 1s и 0s, делая это деление в целочисленном пространстве.
    • Переписанная в плавающую точку, она будет (int)(30.6 * Month - 91.4). 30.6 - среднее число дней в месяц, за исключением февраля (точнее, повторение 30.63). 91,4 - это почти количество дней в 3 средних не-февральских месяцах. (30,6 * 3 равно 91,8).
    • Итак, давайте удалим 30 и просто сосредоточимся на этом 0.6 дня. Если мы умножим его на количество месяцев, а затем обрезаем до целого числа, получим шаблон 0s и 1s.
      • 0,6 * 0 = 0,0 → 0
      • 0,6 * 1 = 0,6 → 0 (разность 0)
      • 0,6 * 2 = 1,2 → 1 (разница 1)
      • 0,6 * 3 = 1,8 → 1 (разность 0)
      • 0,6 * 4 = 2,4 → 2 (разница 1)
      • 0,6 * 5 = 3,0 → 3 (разница 1)
      • 0,6 * 6 = 3,6 → 3 (разность 0)
      • 0,6 * 7 = 4,2 → 4 (разница 1)
      • 0,6 * 8 = 4,8 → 4 (разность 0)
    • Посмотрите на эту схему различий в праве? То же самое в приведенном выше списке, количество дней в каждом месяце минус 30. Вычитание 91,8 будет компенсировать количество дней в первые три месяца, которые были перенесены в "конец" года, и корректировка он на 0,4 перемещает последовательные разности 1 (0,6 * 4 и 0,6 * 5 в приведенной выше таблице), чтобы выровнять их со смежными месяцами, составляющими 31 день.
    • С февраля наступает "конец" года, нам не нужно иметь дело с его длиной. Это может быть 45 дней (46 в високосный год), и единственное, что нужно было бы изменить, это постоянное количество дней в году, 365.
    • Обратите внимание, что это зависит от модели 30 и 31 месяца. Если бы у нас было два месяца подряд, которые составляли 30 дней, это было бы невозможно.
  • 365 * Year: Дни в год
  • (Year / 4) - (Year / 100) + (Year / 400): плюс один височный день каждые 4 года, минус один раз каждые 100, плюс один каждые 400.
  • + 1721119: Это юлианская дата 2 марта 1 года до нашей эры. Поскольку мы перенесли "начало" календаря с января по март, мы используем это как наше смещение, а не 1 января. Поскольку нет нулевого года, 1 BC получает целочисленное значение 0. Что касается того, почему 2 марта, а не 1 марта, я предполагаю, что из-за того, что в этом месяце расчет все еще немного закончился. Если исходный писатель использовал - 462 вместо - 457 (- 92.4 вместо - 91.4 в математике с плавающей запятой), то смещение было бы до 1 марта.

Ответ 2

В то время как метод

public static double ToJulianDate(this DateTime date) { return date.ToOADate() + 2415018.5; }

работает на современные даты, он имеет значительные недостатки.

Юлианская дата определена для отрицательных дат - например, до нашей эры (до общей эры) и является общей для астрономических расчетов. Вы не можете построить объект DateTime с годом, меньшим 0, и поэтому Julian Date не может быть вычислен для дат BCE с использованием вышеуказанного метода.

Грегорианская календарная реформа 1582 года помещала 11-дневное отверстие в календаре с 4 октября по 15-е. Эти даты не определены ни в юлианском календаре, ни в григорианском календаре, но DateTime принимает их как аргументы. Кроме того, использование вышеуказанного метода не возвращает правильное значение для любой юлианской даты. Эксперименты с использованием System.Globalization.JulianCalendar.ToDateTime() или передача юлианской даты в конструктор DateTime по-прежнему приводят неверные результаты для всех дат до 5 октября 1582.

Следующие подпрограммы, адаптированные из "Астрономических алгоритмов" Жана Миэса, возвращают правильные результаты для всех дат, начинающихся с полудня 1 января -4712, нулевого времени в юлианском календаре. Они также выдают исключение ArgumentOutOfRangeException, если недействительная дата передается.

 public class JulianDate
{
    public static bool isJulianDate(int year, int month, int day)
    {
        // All dates prior to 1582 are in the Julian calendar
        if (year < 1582)
            return true;
        // All dates after 1582 are in the Gregorian calendar
        else if (year > 1582)
            return false;
        else
        {
            // If 1582, check before October 4 (Julian) or after October 15 (Gregorian)
            if (month < 10)
                return true;
            else if (month > 10)
                return false;
            else
            {
                if (day < 5)
                    return true;
                else if (day > 14)
                    return false;
                else
                    // Any date in the range 10/5/1582 to 10/14/1582 is invalid 
                    throw new ArgumentOutOfRangeException(
                        "This date is not valid as it does not exist in either the Julian or the Gregorian calendars.");
            }
        }
    }

    static private double DateToJD(int year, int month, int day, int hour, int minute, int second, int millisecond)
    {
        // Determine correct calendar based on date
        bool JulianCalendar = isJulianDate(year, month, day);

        int M = month > 2 ? month : month + 12;
        int Y = month > 2 ? year : year - 1;
        double D = day + hour/24.0 + minute/1440.0 + (second + millisecond / 1000.0)/86400.0;
        int B = JulianCalendar ? 0 : 2 - Y/100 + Y/100/4;

        return (int) (365.25*(Y + 4716)) + (int) (30.6001*(M + 1)) + D + B - 1524.5;
    }

    static public double JD(int year, int month, int day, int hour, int minute, int second, int millisecond)
    {
        return DateToJD(year, month, day, hour, minute, second, millisecond);
    }


    static public double JD(DateTime date) 
    {
        return DateToJD(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, date.Millisecond);
    }
}

Ответ 3

Объяснение Дэвида Яу объясняется, но расчет кумулятивного числа дней в году за месяцы до данного месяца является антиинтуитивным. Если вы предпочитаете массив целых чисел, чтобы сделать алгоритм более понятным, это будет делать:

    /*
     * convert magic numbers created by:
     *    (153*month - 457)/5) 
     * into an explicit array of integers
     */
    int[] CumulativeDays = new int[]
    {
        -92   // Month = 0  (Should not be accessed by algorithm)
      , -61   // Month = 1  (Should not be accessed by algorithm)
      , -31   // Month = 2  (Should not be accessed by algorithm)
      ,   0   // Month = 3  (March)
      ,  31   // Month = 4  (April)
      ,  61   // Month = 5  (May)
      ,  92   // Month = 6  (June)
      , 122   // Month = 7  (July)
      , 153   // Month = 8  (August)
      , 184   // Month = 9  (September)
      , 214   // Month = 10 (October)
      , 245   // Month = 11 (November)
      , 275   // Month = 12 (December)
      , 306   // Month = 13 (January, next year)
      , 337   // Month = 14 (February, next year)
    };

и первые три строки вычисления затем становятся:

  int julianDay = day
                  + CumulativeDays[month]
                  + 365*year
                  + (year/4)

Выражение

(153*month - 457)/5)

хотя и производит точно такую ​​же последовательность, что и те же целые числа, что и приведенный выше массив для значений в диапазоне: от 3 до 14; включительно и делает это без каких-либо требований к хранению. Недостаток требований к хранению - это только добродетель при вычислении кумулятивного количества дней в таком и запутанном виде.

Ответ 4

В моем коде для модифицированного Julian Date используется тот же алгоритм, но на конце появляется другое магическое число, так что итоговое значение соответствует Модифицированной Юлианской Дате, показанной на Wikipedia. Я использую этот же алгоритм в течение как минимум 10 лет как ключ для ежедневных временных рядов (изначально на Java).

public static int IntegerDate(DateTime date)
    {
        int Month = date.Month;
        int Day = date.Day;
        int Year = date.Year;

        if (Month < 3)
        {
            Month = Month + 12;
            Year = Year - 1;
        }
        //modified Julian Date
        return Day + (153 * Month - 457) / 5 + 365 * Year + (Year / 4) - (Year / 100) + (Year / 400) - 678882;
    }

Обратный расчет имеет больше магических чисел для вашего развлечения:

public static DateTime FromDateInteger(int mjd)
    {
        long a = mjd + 2468570;
        long b = (long)((4 * a) / 146097);
        a = a - ((long)((146097 * b + 3) / 4));
        long c = (long)((4000 * (a + 1) / 1461001));
        a = a - (long)((1461 * c) / 4) + 31;
        long d = (long)((80 * a) / 2447);
        int Day = (int)(a - (long)((2447 * d) / 80));
        a = (long)(d / 11);
        int Month = (int)(d + 2 - 12 * a);
        int Year = (int)(100 * (b - 49) + c + a);
        return new DateTime(Year, Month, Day);
    }

Ответ 5

Если кому-то нужно конвертировать из юлианской даты в DateTime, см. ниже:

public static DateTime FromJulianDate(double julianDate)
{
    return DateTime.FromOADate(julianDate - 2415018.5);
}