在C#中将DateTime转换为Juliandate(ToOADate Safe?)
我需要从标准格里历date转换为Julian日数。
我没有在C#中看到任何logging直接做到这一点,但我发现许多post(同时谷歌search)build议使用ToOADate 。
ToOADate上的文档不build议这是Juliandate的有效转换方法。
任何人都可以澄清,如果这个函数将准确地执行转换,或者可能是一个更合适的方法将DateTime转换为Julian格式的string。
这提供了与维基百科的Julian Day页面validation时的预期号码
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; }
但是,这并不了解所使用的魔法数字。
谢谢
参考文献:
- DateTime.ToOADate方法
OADate与Julian Dates类似,但使用不同的起点(1899年12月30日,公元前4713年1月1日)和另一个“新的一天”。 Julian Dates认为中午是新的一天的开始,OADates使用现代定义,午夜。
朱利安1899年12月30日午夜的date是2415018.5。 这个方法应该给你正确的值:
public static double ToJulianDate(this DateTime date) { return date.ToOADate() + 2415018.5; }
至于algorithm:
-
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。 他们通过在整数空间中进行分割来创build1和0的模式。
- 重写为浮点数,它会是
(int)(30.6 * Month - 91.4)
。 30.6是每月的平均天数,不包括2月(重复30.63)。 91.4几乎是非2月份平均3个月的天数。 (30.6 * 3是91.8)。 - 所以,让我们删除30个,只关注0.6天。 如果我们乘以月份数,然后截断为一个整数,我们将得到一个0和1的模式。
- 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天的月份alignment。
- 由于二月份已经到了年底,所以我们不需要处理它的长度。 这可能是45天(闰年46),唯一需要改变的是一年365天的不变。
- 请注意,这依赖于30个月和31个月的模式。 如果我们连续两个月是30天,这是不可能的。
-
365 * Year
:每年的天数 -
(Year / 4) - (Year / 100) + (Year / 400)
:每4年加一闰日,每100减1,再加400。 -
+ 1721119
:这是公元前一世纪三月二日的朱利安date。 由于我们把日历的“开始”从一月份移到三月份,我们用这个作为抵消,而不是1月1日。 由于没有零年,所以1 BC获得整数0.至于为什么3月2日,而不是3月1日,我猜这是因为整个月的计算结果还是有点closures。 如果原作者使用- 462
而不是- 457
(- 92.4
而不是- 91.4
浮点math),那么抵消将是到3月1日。
而方法
public static double ToJulianDate(this DateTime date) { return date.ToOADate() + 2415018.5; }
为现代date工作,它有显着的缺点。
Juliandate是为负date定义的,即BCE(在共同时代之前)date,在天文计算中是常见的。 您不能构造年份小于0的DateTime对象,因此无法使用上述方法为BCEdate计算Juliandate。
公历改革1582年10月4日至15日, 这些date未在Julian日历或公历中定义,但DateTime接受它们作为参数。 此外,使用上述方法不会返回任何Juliandate的正确值。 使用System.Globalization.JulianCalendar.ToDateTime()或将JulianCalendar时代传递给DateTime构造函数的实验在1582年10月5日之前的所有date仍然会产生不正确的结果。
从Jean Meeus的“天文algorithm”改编的以下例程从Julian日历上的零时间-41212年1月1日中午开始的所有date返回正确的结果。 如果传递一个无效的date,他们也会抛出一个ArgumentOutOfRangeExceptionexception。
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); } }
David Yaw的解释是现实的,但是计算给定月份之前几个月的一年的累计天数是反直觉的。 如果你喜欢一个整数数组,使algorithm更清晰,那么这将做到:
/* * 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)
expression方式
(153*month - 457)/5)
虽然产生与上面的数组完全相同的序列相同的整数值范围:3到14; 包容性的,并且没有存储要求。 缺乏存储要求只是在以这种混淆的方式计算累计天数方面的优点。
我修改的Julian Date的代码使用相同的algorithm,但在结尾使用了不同的幻数,以便得到的值与维基百科上显示的修改的Julian Date相匹配。 至less10年来我一直在使用这个相同的algorithm作为日常时间序列(原来是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); }
如果有人需要从儒略date转换为date时间,请参阅下面的内容:
public static DateTime FromJulianDate(double julianDate) { return DateTime.FromOADate(julianDate - 2415018.5); }