32

我需要将标准公历日期转换为儒略日数。

我在 C# 中没有看到任何文档可以直接执行此操作,但我发现很多帖子(在谷歌搜索时)建议使用ToOADate

ToOADate上的文档不建议将此作为儒略日期的有效转换方法。

任何人都可以澄清这个函数是否会准确地执行转换,或者可能是更合适的方法将 DateTime 转换为 Julian 格式的字符串。


当根据Wikipedia 的 Julian Day 页面进行验证时,为我提供了预期的数字

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

然而,这并不是对所使用的幻数的理解。

谢谢


参考:

4

9 回答 9

87

OADate 类似于 Julian Dates,但使用不同的起点(1899 年 12 月 30 日与公元前 4713 年 1 月 1 日)和不同的“新的一天”点。Julian Dates 认为中午是新一天的开始,OADates 使用现代定义,午夜。

1899 年 12 月 30 日午夜的儒略日是 2415018.5。此方法应该为您提供正确的值:

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,但是在 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 几乎是 3 个平均非 2 月的天数。(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 天的相邻月份对齐。
    • 由于二月现在是一年的“结束”,我们不需要处理它的长度。它可能有 45 天(闰年为 46 天),唯一需要改变的是一年中的天数常数 365。
    • 请注意,这依赖于 30 个月和 31 个月的模式。如果我们连续两个月是 30 天,这是不可能的。
  • 365 * Year: 每年的天数
  • (Year / 4) - (Year / 100) + (Year / 400): 每 4 年加一个闰日,每 100 年减一个,每 400 年加一个。
  • + 1721119: 这是公元前 1 年 3 月 2 日的儒略日。由于我们将日历的“开始”从 1 月移至 3 月,因此我们将其用作偏移量,而不是 1 月 1 日。由于没有零年,所以 1 BC 得到整数值 0。至于为什么 3 月 2 日而不是 3 月 1 日,我猜这是因为整月的计算到最后还是有点偏离。如果原始作者使用- 462而不是- 457(- 92.4而不是- 91.4在浮点数学中),那么偏移量将是 3 月 1 日。
于 2011-03-10T02:33:39.647 回答
11

虽然方法

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

适用于现代日期,它有很大的缺点。

儒略日期是为负日期定义的 - 即 BCE(普通纪元之前)日期,并且在天文计算中很常见。您不能构造年份小于 0 的 DateTime 对象,因此无法使用上述方法为 BCE 日期计算儒略日期。

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

以下例程改编自 Jean Meeus 的“天文算法”,从儒略历上的 1 月 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);
    }
}
于 2013-01-28T02:05:10.890 回答
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)
    };

然后计算的前三行变为:

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

表达方式

(153*month - 457)/5)

尽管为范围内的值生成与上述数组完全相同的序列相同的整数:3 到 14;包容性,并且没有存储要求。在以这种模糊的方式计算累积天数时,没有存储要求只是优点。

于 2014-06-22T16:54:37.780 回答
4

如果有人需要将Julian 日期转换DateTime ,请参见下文:

public static DateTime FromJulianDate(double julianDate)
{
    return DateTime.FromOADate(julianDate - 2415018.5);
}
于 2016-12-28T20:39:47.137 回答
1

我修改 Julian Date 的代码使用相同的算法,但最后使用了不同的幻数,因此结果值与Wikipedia上显示的 Modified Julian Date 匹配。作为每日时间序列的关键(最初在 Java 中),我已经使用相同的算法至少 10 年了。

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);
    }
于 2016-11-11T14:45:32.703 回答
0

我在微控制器中使用了一些计算,但只需要 2000 到 2255 之间的年数。这是我的代码:

typedef struct {
    unsigned int8   seconds;    // 0 to 59
    unsigned int8   minutes;    // 0 to 59
    unsigned int8   hours;      // 0 to 23  (24-hour time)
    unsigned int8   day;        // 1 to 31
    unsigned int8   weekday;    // 0 = Sunday, 1 = Monday, etc.
    unsigned int8   month;      // 1 to 12
    unsigned int8   year;       // (2)000 to (2)255
    unsigned int32  julian;     // Julian date
} date_time_t;

    

// 从 DD-MM-YY HH:MM:SS 转换为 JulianTime

void JulianTime(date_time_t * dt)
{
    unsigned int8   m, y;

    y = dt->year;
    m = dt->month;
    if (m > 2) m -= 3;
    else {
        m +=  9;
        y --;
    }
    dt->julian  = ((1461 * y) / 4) + ((153 * m + 2) / 5) + dt->day;
    dt->weekday = ( dt->julian + 2 ) % 7;
    dt->julian  = (dt->julian * 24) + (dt->hours   ); 
    dt->julian  = (dt->julian * 60) + (dt->minutes );     
    dt->julian  = (dt->julian * 60) + (dt->seconds );     
}

// 从 JulianTime 反转到 DD-MM-YY HH:MM:SS

void GregorianTime(date_time_t *dt) 
{
    unsigned int32  j = dt->julian;

    dt->seconds = j % 60;
    j /= 60;
    dt->minutes = j % 60;
    j /= 60;
    dt->hours   = j % 24;
    j /= 24;
    dt->weekday = ( j + 2 ) % 7; // Get day of week
    dt->year = (4 * j) / 1461;
    j = j - ((1461 * dt->year) / 4);
    dt->month = (5 * j - 3) / 153;
    dt->day  = j - (((dt->month * 153) + 3) / 5);
    if ( dt->month < 10 )
    {
        dt->month += 3;
    }
    else
    {
        dt->month -= 9;
        dt->year ++;
    }
}

希望这会有所帮助:D

于 2020-09-24T18:33:47.767 回答
0

您链接的维基百科页面包含从儒略历或公历转换的代码。例如,您可以选择转换公历时代之前的日期,这称为“预测公历”。

根据所选的“转换”日历,输出会有所不同。这是因为日历本身是不同的构造,并以不同的方式处理各种对齐/更正。

public enum ConversionCalendar
{
  GregorianCalendar,
  JulianCalendar,
}

public static int ConvertDatePartsToJdn(int year, int month, int day, ConversionCalendar conversionCalendar)
{
  switch (conversionCalendar)
  {
    case ConversionCalendar.GregorianCalendar:
      return ((1461 * (year + 4800 + (month - 14) / 12)) / 4 + (367 * (month - 2 - 12 * ((month - 14) / 12))) / 12 - (3 * ((year + 4900 + (month - 14) / 12) / 100)) / 4 + day - 32075);
    case ConversionCalendar.JulianCalendar:
      return (367 * year - (7 * (year + 5001 + (month - 9) / 7)) / 4 + (275 * month) / 9 + day + 1729777);
    default:
      throw new System.ArgumentOutOfRangeException(nameof(calendar));
  }
}

还可以从 JDN 转换回日期组件:

public static void ConvertJdnToDateParts(int julianDayNumber, ConversionCalendar conversionCalendar, out int year, out int month, out int day)
{
  var f = julianDayNumber + 1401;

  if (conversionCalendar == ConversionCalendar.GregorianCalendar)
    f += (4 * julianDayNumber + 274277) / 146097 * 3 / 4 + -38;

  var eq = System.Math.DivRem(4 * f + 3, 1461, out var er);
  var hq = System.Math.DivRem(5 * (er / 4) + 2, 153, out var hr);

  day = hr / 5 + 1;
  month = ((hq + 2) % 12) + 1;
  year = eq - 4716 + (14 - month) / 12;
}

这些方法是根据维基百科上的代码创建的,所以它们应该可以工作,除非我搞砸了。

于 2021-10-21T15:40:14.077 回答
0

以下方法为您提供从 1995/1/1 00:00:00 开始的儒略日

    /// <summary>
    /// "GetJulianDays" will return a Julian Days starting from date 1 Jan 1995
    /// </summary>
    /// <param name="YYYYMMddHHmmss"></param>
    /// <returns>Julian Day for given date</returns>
    public string GetJulianDays(DateTime YYYYMMddHHmmss)
    {
        string DateTimeInJulianFormat = string.Empty;
        DateTime julianStartDate = new DateTime(1995, 1, 1, 00, 00, 00); //YYYY,MM,dd,HH,mm,ss

        DateTime DateTimeNow = YYYYMMddHHmmss;

        double difference = (DateTimeNow - julianStartDate).TotalDays;

        int totalDays = int.Parse(difference.ToString());

        DateTimeInJulianFormat = string.Format("{0:X}", totalDays);

        return DateTimeInJulianFormat;
    }
于 2021-11-30T10:08:58.813 回答
-2

以下函数将日期转换为与 Tradestation Easylanguage 匹配的儒略日期: public double ToJulianDate(DateTime date) { return date.ToOADate(); }

于 2021-06-22T23:52:56.777 回答