2011-03-09 165 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); 
} 
相關問題