Преобразование 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);
}