2011-03-09 207 views
24

我需要从标准格里高利日期转换为朱利安天数。将DateTime转换为Julian在C#中的日期(ToOADate Safe?)

我在C#中没有看到任何记录直接做到这一点,但我发现许多帖子(同时谷歌搜索)暗示使用ToOADate

关于ToOADate的文档不建议将此作为Julian日期的有效转换方法。

任何人都可以澄清,如果此功能将执行转换准确,或者也许更适当的方法将DateTime转换为朱利安格式的字符串。


This为我提供了预期数量时,对Wikipedia's Julian Day page

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

验证然而,这是没有被使用的幻数的理解。

感谢


参考文献:

回答

74

OADate类似于朱利安日期,但使用不同的起点(1899年12月30日,主场迎战一月公元前4713年)以及另一个“新的一天”。 Julian Dates认为中午是新的一天的开始,OADates在午夜使用现代定义。

朱利安1899年12月30日午夜的日期是2415018.5。这个方法应该给你正确的价值观:

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

至于算法:

  • if (Month < 3) ...:为了使幻数我们的工作权,他们把2月的的“结束”年。
  • (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。他们通过在整数空间中进行分割来创建1和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天的月份对齐。
    • 由于2月份现在处于年底,所以我们不需要处理它的长度。它可以是45天(闰年为46),唯一需要改变的是一年中的天数365。
    • 请注意,这依赖于30和31个月的日子。如果我们连续两个月是30天,这是不可能的。
  • 365 * Year:每年的天数
  • (Year/4) - (Year/100) + (Year/400):加每4年一个闰日,减去一个每100个,加上每一个400
  • + 1721119:这是3月2日的儒略日,BC 1 。由于我们将日历的“开始”从1月份移至3月份,因此我们将此用作抵消日期,而不是1月1日。由于没有零年,所以1 BC获得整数值0.至于为什么3月2日而不是3月1日,我猜这是因为整个月的计算结果还是有点偏离。如果原作者使用- 462而不是- 457- 92.4而不是- 91.4在浮点数学中),那么偏移量应该是3月1日。
+0

谢谢你这个详细的解释。 – cweston 2011-03-10 14:21:17

+0

+1。真棒帖子。 – BSalita 2012-02-06 11:27:58

+1

对不起,我不喜欢数学,但你做得很好。谢谢 – fiberOptics 2012-02-22 02:37:23

7

虽然方法

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

作品为现代的日期,它有显著的缺点。

Julian日期定义为负日期 - 即BCE(在共同时代之前)日期,并且在天文计算中很常见。您无法构造年份小于0的DateTime对象,因此无法使用上述方法为BCE日期计算Julian日期。

1582年的公历改革在10月4日至15日的日历中出现了11天的漏洞。这些日期没有在Julian日历或格里历日历中定义,但DateTime接受它们作为参数。此外,使用上述方法不会为任何儒略日期返回正确的值。使用System.Globalization.JulianCalendar.ToDateTime()或将JulianCalendar时代传递给DateTime构造函数的实验在1582年10月5日之前的所有日期仍然会产生不正确的结果。

以下例程改编自Jean Meeus的“天文算法”,从Julian日历上的零时间-4月12日开始的所有日期返回正确的结果。如果传递了无效日期,他们还会抛出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); 
    } 
} 
+0

虽然这通常起作用,但是D的公式需要读取......(second + millisecond/1000.0)...',否则结果对于包含毫秒的DateTime值是关闭的。 – JBartlau 2017-01-19 14:56:55

4

David Yaw的解释是现货,但是计算给定月份之前几个月的一年的累计天数是反直觉的。如果你喜欢一个整数数组,使算法更清楚,那么这将做到:

/* 
    * 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) 
    }; 

和第一THRE线的计算则变成:

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

表达

(153*month - 457)/5) 

虽然产生与上述数组完全相同的序列,但其值范围为:3到14;包容性的,并且没有存储要求。在以这种混淆的方式计算累计天数方面,缺少存储要求只是美德。

0

我的修改的Julian Date代码使用相同的算法,但在结尾使用了不同的幻数,以便生成的值与Wikipedia上显示的修改的Julian Date相匹配。至少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); 
    } 
0

如果有人需要 Julian日期为DateTime转换,见下图:

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