Преобразование DateTime в Julian Date в C# (Tooadate Safe?)

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

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

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

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


этой предоставляет мне ожидаемое число при проверке против Джулиан день страницы Википедии

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;
}

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

спасибо


ссылки:

5 ответов


OADate похож на Юлианские даты, но использует другую отправную точку (30 декабря 1899 года против 1 января 4713 года до н. э.) и другую точку "нового дня". Юлианские даты считают полдень началом нового дня, Оадаты используют современное определение-полночь.

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, но после этого корректировки в случае, если заявление он становится 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. Они создают этот шаблон из 1 и 0, выполняя это деление в целочисленном пространстве.
    • переписано с плавающей запятой, это было бы (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 в плавающей точкой math), то смещение было бы до 1 марта.

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

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

работает на современные даты, имеет существенные недостатки.

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

реформы григорианского календаря 1582 положила 11 день в календаре с 4 по 15 октября. Эти даты не определены ни в юлианском, ни в григорианском календаре, но DateTime принимает их в качестве аргументов. Кроме того, использование вышеуказанного метода не возвращает правильное значение для любой юлианской даты. Эксперименты с использованием системы.Глобализация.JulianCalendar.ToDateTime () или передача эры JulianCalendar в конструктор 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);
    }
}

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

    /*
     * 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; включительно и делает так без требований к хранения. Отсутствие требований к хранению является только достоинством при расчете совокупного количества дней таким и запутанным способом.


Если кому-то нужно конвертировать С Джулиан дата в DateTime, см. ниже:

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

мой код для измененной юлианской даты использует тот же алгоритм, но другое магическое число в конце, так что полученное значение соответствует измененной юлианской дате, показанной на Википедия. Я использую этот же алгоритм, по крайней мере, 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);
    }